codebee 0.1.43 → 0.1.45

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,18 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## v0.1.45(2026-09-22)
10
+
11
+ ### 🛠 问题修复
12
+
13
+ - 智能体读取超大文件(超 64KB 的日志/长文)不再只给开头:改为保留开头 44KB 与结尾 16KB、中段标注省略——报错和结论常在文件末尾,此前最关键的信息会被截掉;结尾处的中文也不会因字节截断而乱码。
14
+
15
+ ## v0.1.44(2026-09-22)
16
+
17
+ ### ✨ 新功能
18
+
19
+ - 经验库教训分型可视化:绿色「做法」徽章标记程序性记忆(从成功任务提炼的有效步骤),与普通「教训」一眼区分,悬停可见说明。
20
+
9
21
  ## v0.1.43(2026-09-22)
10
22
 
11
23
  ### ✨ 新功能
package/README.md CHANGED
@@ -21,10 +21,15 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
21
21
  不会把你的任务内容交给任何第三方。
22
22
 
23
23
  <!-- relnotes:start -->
24
+ ### 🛠 问题修复 · Fixes
25
+
26
+ - 智能体读超大文件不再只给开头:保留开头 44KB+结尾 16KB、中段标注省略——文件末尾的报错与结论不再被截掉,结尾中文不乱码。
27
+ - Reading oversized files no longer keeps only the head: 44KB head + 16KB tail with the middle elided — end-of-file errors and conclusions survive, and trailing Chinese text stays intact.
28
+
24
29
  ### ✨ 新功能 · New
25
30
 
26
- - 经验库新增「有效做法」记忆:高分一次通过的任务会把「做对了什么」提炼成可复用步骤,下次同类任务自动注入照做。
27
- - The lesson library now also captures "what worked": high-scoring first-pass runs distill reusable procedures, auto-injected into similar future tasks.
31
+ - 经验库新增「有效做法」记忆:高分一次通过的任务会把「做对了什么」提炼成可复用步骤,下次同类任务自动注入照做;绿色「做法」徽章一眼区分。
32
+ - The lesson library now also captures "what worked": high-scoring first-pass runs distill reusable procedures, auto-injected into similar future tasks — marked with a green "Best practice" badge.
28
33
 
29
34
  - 新任务类型「标书编制」:上传招标文件,AI 按评分标准逐条应答起草投标文件;资质不虚构(缺失标注待补)、应答先结论、全文自查废标风险项。
30
35
  - New "Bid Document" task type: upload the tender file and the AI drafts point-by-point responses against scoring criteria — no fabricated credentials, conclusion-first answers, and a full disqualification-risk sweep.
@@ -23,6 +23,8 @@ from . import modelhub, runner
23
23
 
24
24
  MAX_TOOL_ITERS = 16 # 单步工具循环上限(防模型打转)
25
25
  READ_MAX_BYTES = 64 * 1024 # read_file 单次读取上限
26
+ READ_HEAD_BYTES = 44 * 1024 # TokenJuice 借鉴(openhuman):超限文件头尾保留、
27
+ READ_TAIL_BYTES = 16 * 1024 # 中段省略——日志/代码的报错常在尾部,纯截头会丢关键信息
26
28
  LIST_MAX_ENTRIES = 200
27
29
  MAX_IMAGES = 8 # 单次最多随消息传几张图(请求体防爆)
28
30
  IMAGE_MAX_EDGE = 1568 # 长边上限(视觉模型通行建议值,超出等比缩)
@@ -268,7 +270,32 @@ def _tool_read_file(workdir, args):
268
270
  return "(文件不存在: %s)" % rel
269
271
  data = p.read_bytes()[:READ_MAX_BYTES + 1]
270
272
  truncated = len(data) > READ_MAX_BYTES
271
- head = data[:READ_MAX_BYTES]
273
+ if truncated:
274
+ # 头尾保留、中段省略(TokenJuice 借鉴):头 44k 给结构/开头上下文,
275
+ # 尾 16k 给报错/结论(日志与代码的关键信息常在末尾),中段标注省略量
276
+ try:
277
+ size = p.stat().st_size
278
+ except OSError:
279
+ size = len(data)
280
+ with open(p, "rb") as fh:
281
+ head = fh.read(READ_HEAD_BYTES)
282
+ fh.seek(max(0, size - READ_TAIL_BYTES))
283
+ tail = fh.read(READ_TAIL_BYTES)
284
+ head = _trim_partial_utf8(head)
285
+ # 尾段开头的残缺多字节序列剥头(对偶于 _trim_partial_utf8 剥尾)
286
+ while tail:
287
+ try:
288
+ tail.decode("utf-8")
289
+ break
290
+ except UnicodeDecodeError:
291
+ tail = tail[1:]
292
+ text = runner.decode_output(head) \
293
+ + "\n…(中段省略 %d 字节;头 %dKB + 尾 %dKB 保留)\n" % (
294
+ max(0, size - READ_HEAD_BYTES - READ_TAIL_BYTES),
295
+ READ_HEAD_BYTES // 1024, READ_TAIL_BYTES // 1024) \
296
+ + runner.decode_output(tail)
297
+ return "…(文件超 64KB,已头尾保留读取)\n" + text
298
+ head = data
272
299
  if not head:
273
300
  return "(空文件)"
274
301
  if _looks_binary(head):
@@ -284,10 +311,8 @@ def _tool_read_file(workdir, args):
284
311
  return ("(二进制文件,无法按文本读取: %s — %s,%d B。read_file 只支持文本文件;"
285
312
  "请如实告知用户该文件内容无法以文本方式查看,不要反复重读。)"
286
313
  % (rel, fmt, size))
287
- if truncated:
288
- head = _trim_partial_utf8(head)
289
314
  text = runner.decode_output(head)
290
- return ("…(超过 64KB 已截断)\n" if truncated else "") + text
315
+ return text
291
316
 
292
317
 
293
318
  def _tool_write_file(workdir, args):
@@ -307,7 +332,7 @@ def _tool_write_file(workdir, args):
307
332
  TOOLS_SPEC = [
308
333
  {"name": "list_files", "description": "列出工作目录(或其子目录)下的文件",
309
334
  "args": {"path": "子目录相对路径,留空=根目录"}},
310
- {"name": "read_file", "description": "读取工作目录内一个文本文件(UTF-8/GBK 自动识别,超 64KB 截断;图片等二进制文件无法读取)",
335
+ {"name": "read_file", "description": "读取工作目录内一个文本文件(UTF-8/GBK 自动识别,超 64KB 时保留开头与结尾、中段省略;图片等二进制文件无法读取)",
311
336
  "args": {"path": "文件相对路径"}},
312
337
  {"name": "write_file", "description": "把文本内容写入工作目录内一个文件(UTF-8,父目录自动创建)",
313
338
  "args": {"path": "文件相对路径", "content": "完整文本内容"}},
@@ -330,7 +330,10 @@ def upsert_lesson(scope, title, content, source="", category=None, dim=None):
330
330
  it = {"id": lid, "scope": scope, "title": title, "content": content,
331
331
  "source": source, "hits": 0, "seen": 1, "enabled": True,
332
332
  "category": cat,
333
- "created_at": _now(), "kind": "lesson"}
333
+ "created_at": _now(),
334
+ # 稳定 token(procedure=程序性做法 / lesson=规避性教训);
335
+ # 展示层 view() 翻译成「做法/教训」,持久层不用中文防改文案伤数据
336
+ "kind": ("procedure" if title.startswith("做法:") else "lesson")}
334
337
  items.append(it)
335
338
  _save(data)
336
339
  return it
@@ -676,8 +679,18 @@ def learn_async(run_id):
676
679
 
677
680
  def view():
678
681
  """经验库总览(给 UI/API)。categories:闭集枚举 + 实际出现过的分类,
679
- counts 给每类条数,供 UI 下拉过滤与计数显示。"""
680
- lessons = list_lessons()
682
+ counts 给每类条数,供 UI 下拉过滤与计数显示。
683
+ lessons 每条带展示 kind:「做法」(程序性记忆)/「教训」(规避性)——
684
+ UI 用不同徽章区分(openhuman 记忆可视化借鉴)。持久层是稳定 token
685
+ (procedure/lesson,老数据可能缺失),此处统一翻译+派生兜底。"""
686
+ def _kind(x):
687
+ k = x.get("kind")
688
+ if k == "procedure":
689
+ return "做法"
690
+ if k == "lesson":
691
+ return "教训"
692
+ return "做法" if str(x.get("title") or "").startswith("做法:") else "教训"
693
+ lessons = [{**x, "kind": _kind(x)} for x in list_lessons()]
681
694
  counts = {}
682
695
  for x in lessons:
683
696
  c = x.get("category") or LESSON_UNCATEGORIZED
package/app/ui/app.js CHANGED
@@ -8431,6 +8431,7 @@ function renderSkills() {
8431
8431
  $("skill-lessons").innerHTML = shown.map((x) =>
8432
8432
  '<div class="card' + (x.enabled === false ? " off" : "") + '"><div class="head">' +
8433
8433
  '<span class="name">' + esc(t(x.title)) + "</span>" +
8434
+ (x.kind === "做法" ? '<span class="tag ok" title="' + t("已验证有效的做法(程序性记忆)") + '">' + t("做法") + "</span>" : "") +
8434
8435
  (x.category ? '<span class="tag cat" style="color:' + (CAT_COLOR[x.category] || "var(--muted)") + '" ' +
8435
8436
  'onclick="skillCatFilter(\'' + esc(x.category) + '\')" title="' + t("只看该类") + '">' + esc(t(x.category)) + "</span>" : "") +
8436
8437
  '<span class="tag">' + esc(x.scope === "*" ? t("通用") : x.scope) + "</span>" +
package/app/ui/i18n.js CHANGED
@@ -1517,6 +1517,8 @@
1517
1517
  "知道了": "Got it",
1518
1518
  "新版本更新内容": "What's new in the new version",
1519
1519
  "已注入 ": "injected ",
1520
+ "做法": "Best practice",
1521
+ "已验证有效的做法(程序性记忆)": "Verified working procedure (procedural memory)",
1520
1522
  "已清除 ": "Cleared ",
1521
1523
  "已禁用厂商 {0}:链降级自动跳过,绑定页可重新启用": "Provider {0} disabled: chain fallback skips it — re-enable on the Bindings page",
1522
1524
  "已禁用模型 {0} · {1},链降级自动跳过;绑定页可重新启用": "Model {0} · {1} disabled: chain fallback skips it — re-enable on the Bindings page",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebee",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
5
  "license": "MIT",
6
6
  "bin": {