codebee 0.1.37 → 0.1.38

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,12 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## v0.1.38(2026-09-21)
10
+
11
+ ### ✨ 新功能
12
+
13
+ - 对话里发出的图片附件现在直接以 96×96 缩略图展示在气泡中(此前只有一串文件名),点击可放大查看;发图片时会提醒一次:若当前模型未开启「图」输入,模型将看不到这张图(可在模型接入页为模型打开图片能力)。
14
+
9
15
  ## v0.1.37(2026-09-21)
10
16
 
11
17
  ### ✨ 新功能
@@ -101,7 +101,33 @@ def execute_step(session, run_agent_fn, prompt, *, model: str = "",
101
101
  log.info("overflow but no compaction progress (gen %s->%s), no retry",
102
102
  gen0, gen1)
103
103
  return result, False
104
- # 压缩生效:surface 已折叠,重新派生消息后用同一 prompt 重试一次
105
- log.info("compaction advanced gen %s->%s, retrying step", gen0, gen1)
106
- result2 = run_agent_fn(prompt, **kwargs)
104
+ # 压缩生效:surface 已折叠。**真正替换 CLI 请求上下文**(2026-09-21
105
+ # 清最老待深挖项:此前重试仍发原 prompt + 续旧 resume 会话——摘要只进
106
+ # 审计日志,CLI 看到的上下文一点没小,重试必然再爆):
107
+ # ①重试 prompt 改用 session 派生转录(折叠摘要 + 近尾消息,体量骤降);
108
+ # ②弃 resume(旧会话本体仍是膨胀态,续上就前功尽弃)。
109
+ msgs = []
110
+ try:
111
+ msgs = session.derive_messages()
112
+ except Exception:
113
+ log.debug("derive_messages failed on retry", exc_info=True)
114
+ kwargs2 = dict(kwargs)
115
+ if msgs:
116
+ transcript = "\n\n".join("[%s] %s" % (m.get("role"), m.get("content"))
117
+ for m in msgs)
118
+ if len(transcript) > 24_000:
119
+ transcript = transcript[-24_000:]
120
+ retry_prompt = transcript
121
+ # 本步指令必须在场:被折叠/截断掉就显式补在尾部(缺失指令比重复严重)
122
+ tail = transcript[-2000:]
123
+ if prompt[:400] not in tail:
124
+ retry_prompt = transcript + "\n\n" + prompt
125
+ kwargs2.pop("resume", None)
126
+ log.info("compaction advanced gen %s->%s, retrying with derived "
127
+ "context (%d msgs, resume dropped)", gen0, gen1, len(msgs))
128
+ result2 = run_agent_fn(retry_prompt, **kwargs2)
129
+ else:
130
+ log.info("compaction advanced gen %s->%s but no surface derived, "
131
+ "retrying same prompt", gen0, gen1)
132
+ result2 = run_agent_fn(prompt, **kwargs)
107
133
  return result2, True
package/app/ui/app.js CHANGED
@@ -165,16 +165,18 @@ window.toggleModelMenu = toggleModelMenu;
165
165
  if (b.dataset.m === undefined) {
166
166
  // 点厂商 = 厂商+推荐模型一起定(用户反馈「只选了厂商」):带出该厂商
167
167
  // 排序最前的模型并展开模型级菜单,可立即改选;已是当前厂商则只钻取。
168
- // 注意先 renderDirectModelPicker 重建模型 options,再赋值才生效。
168
+ // 顺序关键:先 picker 重建 options 再赋值——select.value 对不存在的
169
+ // option 会静默忽略(真机「没法选模型」根因之一)
169
170
  const pid = b.dataset.p;
170
171
  if ($("f-direct-provider").value !== pid) {
172
+ renderDirectModelPicker();
171
173
  $("f-direct-provider").value = pid;
172
174
  renderDirectModelPicker();
173
175
  const p = directProviders().find((x) => x.id === pid);
174
- const names = p ? (p.models || []).filter((m) => m.enabled !== false && !m.hidden)
176
+ const names = p ? (p.models || []).filter((m) => !m.hidden)
175
177
  .map((m) => m.name).filter(Boolean) : [];
176
178
  const ms = $("f-direct-model-name");
177
- if (names.length && names.includes(names[0])) ms.value = names[0];
179
+ if (names.length && names[0]) ms.value = names[0];
178
180
  cmpDirectBtnSync();
179
181
  }
180
182
  cmpModelProv = pid;
@@ -338,6 +340,7 @@ function onTypeChange() {
338
340
  }
339
341
 
340
342
  function directProviders() {
343
+ /* 停用厂商不列出(用户拍板 2026-09-21「停用的不弄出来」);只列启用且有 key 的 */
341
344
  return (S.providers || []).filter((p) => p.enabled !== false && p.api_key);
342
345
  }
343
346
 
@@ -351,7 +354,7 @@ function renderDirectModelPicker() {
351
354
  if (prev && provs.some((p) => p.id === prev)) ps.value = prev;
352
355
  const p = provs.find((x) => x.id === ps.value);
353
356
  const oldModel = ms.value;
354
- const names = p ? (p.models || []).filter((m) => m.enabled !== false && !m.hidden)
357
+ const names = p ? (p.models || []).filter((m) => !m.hidden)
355
358
  .map((m) => m.name).filter(Boolean) : [];
356
359
  ms.innerHTML = '<option value="">' + t("随厂商推荐") + '</option>' + names.map((name) =>
357
360
  '<option value="' + esc(name) + '">' + esc(name) + "</option>").join("");
@@ -1145,9 +1148,10 @@ async function poll() {
1145
1148
  S.providers = models.providers; S.bindings = models.bindings;
1146
1149
  S.modelCatalog = models.catalog || [];
1147
1150
  S.sourceNames = models.source_names || S.sourceNames || {};
1148
- if ($("f-direct-model") && !$('f-direct-model').classList.contains("hidden")) {
1149
- renderDirectModelPicker();
1150
- }
1151
+ // /api/models 到手即重建对话模型选项(无条件):旧逻辑只在「更多选项」
1152
+ // 旧块可见时重建——旧块隐藏后选项永远只有「自动推荐」,厂商写值全部
1153
+ // 静默失效(2026-09-21 用户实测「没法选模型」根因)
1154
+ renderDirectModelPicker();
1151
1155
  // 供应商集合/启停/密钥变了才重拉编排者:否则「生效中」状态点会在停用厂商后失真
1152
1156
  const provSig = JSON.stringify((S.providers || []).map((p) => [p.id, p.enabled, !!p.api_key]));
1153
1157
  if (provSig !== S.provSig) { S.provSig = provSig; loadOrchestrator(); }
@@ -6610,7 +6614,10 @@ async function renderChat(run, active) {
6610
6614
  let rel = typeof a === "string" ? a : ((a && (a.path || a.name)) || nm);
6611
6615
  rel = String(rel).replace(/\\/g, "/");
6612
6616
  if (!rel.startsWith("_attachments/")) rel = "_attachments/" + rel.split("/").pop();
6613
- return { nm, rel };
6617
+ const isImg = /\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(nm);
6618
+ const src = isImg && chatRunId
6619
+ ? "/api/runs/" + encodeURIComponent(chatRunId) + "/file?name=" + encodeURIComponent(rel) : "";
6620
+ return { nm, rel, img: src };
6614
6621
  }).filter(Boolean);
6615
6622
  return '<div class="chat-row me">' +
6616
6623
  '<div class="chat-bubble me">' +
@@ -6619,8 +6626,11 @@ async function renderChat(run, active) {
6619
6626
  esc(it.text || t("(仅附件)")) +
6620
6627
  (atts.length
6621
6628
  ? '<div class="chat-atts">' + atts.map((a) =>
6622
- '<span class="att-chip2 att-open" data-att="' + esc(a.rel) +
6623
- '" title="' + esc(t("点击查看")) + '">' + esc(a.nm) + "</span>").join("") + "</div>"
6629
+ (a.img
6630
+ ? '<span class="att-open chat-img" data-att="' + esc(a.rel) + '" title="' + esc(a.nm) + '">' +
6631
+ '<img src="' + esc(a.img) + '" alt="' + esc(a.nm) + '" loading="lazy" draggable="false"></span>'
6632
+ : '<span class="att-chip2 att-open" data-att="' + esc(a.rel) +
6633
+ '" title="' + esc(t("点击查看")) + '">' + esc(a.nm) + "</span>")).join("") + "</div>"
6624
6634
  : "") +
6625
6635
  "</div></div>";
6626
6636
  }
@@ -6883,6 +6893,14 @@ async function chatUploadFiles(files) {
6883
6893
  const r = await api("/api/attachments", { method: "POST",
6884
6894
  body: JSON.stringify({ name: f.name, data: b64 }) });
6885
6895
  chatAtts.push(r.attachment); drawChatAtts();
6896
+ // 图片能力前置提醒:模型 image_in 未开启时收不到图(内置智能体会降级为
6897
+ // 纯文本,模型只能回复「读不到」——与其事后看模型解释,不如发图时提一句)
6898
+ if (/^image\//i.test(String(f.type || "")) || /\.(png|jpe?g|gif|webp|bmp)$/i.test(f.name || "")) {
6899
+ if (!window._chatImgTipShown) {
6900
+ window._chatImgTipShown = true;
6901
+ toast(t("已添加图片。若当前模型未开启「图」输入(模型接入页可开),模型将看不到这张图"));
6902
+ }
6903
+ }
6886
6904
  } catch (e) { toast(t("附件上传失败:") + f.name + " — " + e.message, true); }
6887
6905
  }
6888
6906
  }
package/app/ui/i18n.js CHANGED
@@ -291,6 +291,7 @@
291
291
  "指令已入箱,将在下一个步骤下达": "Directive queued — it will be delivered with the next step",
292
292
  "发送失败:": "Send failed: ",
293
293
  "附件上传失败:": "Upload failed: ",
294
+ "已添加图片。若当前模型未开启「图」输入(模型接入页可开),模型将看不到这张图": "Image added. If the model doesn't have the image-input badge on (enable it on the Models page), it won't see this picture",
294
295
  "读取文件失败": "Failed to read file",
295
296
  ":超过 8MB,已跳过": ": over 8MB, skipped",
296
297
  "附件最多 ": "Max ",
package/app/ui/style.css CHANGED
@@ -5671,3 +5671,10 @@ button.request-busy > * { opacity: .18; }
5671
5671
  background: var(--panel); color: var(--muted); }
5672
5672
  .att-chip.att-image .ax:hover { background: var(--bad); color: #fff; }
5673
5673
  .att-chip.att-image .ax .ico { width: 11px; height: 11px; }
5674
+
5675
+ /* 对话气泡图片附件缩略图(2026-09-21 用户反馈:图片只显示文件名要点开才见):
5676
+ * 96×96 圆角卡,点击仍走 att-open 弹大图 */
5677
+ .chat-img { display: inline-block; cursor: zoom-in; }
5678
+ .chat-img img { display: block; width: 96px; height: 96px; object-fit: cover;
5679
+ border-radius: 10px; border: 1px solid var(--border); background: var(--panel2); }
5680
+ .chat-atts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebee",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
5
  "license": "MIT",
6
6
  "bin": {