Feat/3.0.0 beta1 - #2311
Merged
Merged
Conversation
180 POC 会话 aa352cb4 复盘:主图并行委派 2 个调研子代理,日志显示 `graph=sub turn 29/30`——那是两个子代理**加起来**的数。 根因:deepagents 在建 task 工具时把子代理一次性编译 (subagents.py:584),每次 task 调用都重入同一个 runnable,而中间件的 `_turn_count` 是实例属性且从不重置,于是 N 次委派共吃一份 30 轮额度, 第二个子代理一上来就落在软着陆区。注释里只声称「主图与子代理各自独立」, 从未声称多次 task 调用共享——是隐性缺陷,不是设计意图。 改为按 LangGraph 节点 namespace 前缀分桶(有界 LRU,上限 128): - 子代理 → 每次 task 调用一个桶,同一次调用跨轮次恒定、并发调用互不相同; - 主图 → 强制单桶。主图 ns 形如 `model:<uuid>` 每轮都变且不含分隔符, 一旦分桶预算每轮重置,是灾难性回归; - 拿不到 runtime / ns 扁平 → 回落单桶 = 修复前行为,永不劣化。 同时:一次模型调用若只产出 write_todos(纯状态维护)不再扣预算。实测子代理 29 次调用里 10 次是这种空转。两段式记账——软着陆档位仍在调用前决定(它要改写 request),退款放在循环唯一的成功出口,所以瞬时重试/截断补写不会重复退款, 降级轮不退款。退款每桶封顶 10,否则 write_todos 死循环会让计数器永不前进、 软着陆阶梯永不触发,最后死在 GraphRecursionError。 新增 test_subagent_ns_contract.py:纯 langgraph 不调模型,钉住 namespace 的 三条性质。这是升级 langgraph/langchain 时唯一能提前炸出来的守门测试。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
180 POC 会话 aa352cb4 成功交付后仍显示「任务已完成 4/7」,库里还躺着 1 条 IN_PROGRESS + 2 条 NOT_STARTED,而会话状态是 COMPLETED。 linsight_execute_task 实际是 append-only 的「首次规划快照」: - 模型从计划里剪掉的 todo 不产生任何事件。mapper 的注释写着「消失的 todo 标记为 TERMINATED」,实现里却只有一行 `self.ctx.todos = new_projection` ——注释与实现不符; - 三条正常完成路径只 finalize svid 伪任务,从不收口真实 todo 行; - 位置对齐复用旧行 id 时不回写 task_data,标题永远停在第一版草稿。 三处修复: 1. `_set_tasks_failed` 更名 `_terminate_unfinished_tasks`(它写的是 TERMINATED,旧名字既没描述动作也没描述状态),三条完成路径经 `_converge_task_rows_on_completion` 接上。顺序是硬约束:必须在 `_complete_session_pseudo_task` 之后,否则 sweep 会把会话自己那行也标掉。 2. `_diff_todos` 为消失项产出 TaskEnd(status="terminated"),已完成的跳过 (剪掉计划不等于撤销已交付的工作)。**守门点**:`_handle_task_end` 里 `_final_result` 必须收窄为「非 TERMINATED 才赋值」——否则一条被剪掉的 todo 最后到达会经 `_handle_task_completion` 把整个会话判失败。 3. `_save_task_info` 对文案被改写的行刷新 task_data。 前端同步:后端改了前端也不会变——terminated 在 taskStatus 里既非 done 也非 running,渲染成和 not_started 一样的灰圈且仍进分母。TaskPanel 在 `completed && !terminated` 时过滤掉 terminated 行。用户终止/失败两条路径 的观感逐字节不变(有守门测试)。 另:recursion 地板跟随 write_todos 退款上调为 (turns+10)*4+20,并把 initdb_config 模板的 max_steps 提到 600 保持自洽——新增测试直接读模板断言 它不会自己触发「max_steps 低于所需」的告警。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
文件工具回显带前导斜杠的 /output/x,而代码执行器的 cwd 就是同一个工作区根,
盘上是 output/x。没人说过这件事,180 POC 会话为此付了两次代价:
正向 open("/skills/…") 直接 FileNotFoundError,花 3 次模型往返才摸清;
反向把执行器里看到的宿主路径喂给 read_file,前导斜杠被无脑剥掉后拼成
workspace/<svid>/root/.cache/… 这种废 key,收尾连续 4 次读图失败。
三件事:
1. 提示词:新增 path_namespace_rules() 供两个执行器共用,并把中文版说明注入
主 system prompt。门控从「skills_present AND has_code_interpreter」双真
改为只看 has_code_interpreter——原来那句唯一的解释藏在技能分支里,没选技能
的会话(正是这次)根本看不到。保留 has_code_interpreter 门控是因为本模块
处处遵守 prompt↔tool lockstep,未绑工具时不得提及工具名(有测试断言)。
e2b 用 include_skills=False:沙箱 copy-in 快照发生在 skills 落地之前,
那里确实没有 skills/,连举例也不能写。
2. normalize_workspace_path 增加可选 file_dir,识别执行器宿主路径前缀。
收益不止「报错更友好」:read 会先查本地缓存 Path(file_dir)/rel,而执行器
就写在那儿,所以折算之后那 4 次读图会直接**读成功**。
只做可证明无歧义的剥离:要求完整 file_dir 前缀且带 / 边界。曾实现过
「同父目录的兄弟任务目录也剥」,被自己的对抗用例证伪(前缀相似的兄弟被
吞掉)后删除——改写路径必须无歧义,猜测只能进错误文案,不能进选文件。
3. advisory 区分读写:/skills、/uploads 是**读**区,open 抛 FileNotFoundError
而已,什么都没丢,沿用写侧「文件被 DISCARDED」的措辞会让模型去追一个根本
不存在的数据丢失问题。并且必须挂到失败返回路径上——绝对路径读必然
exitcode≠0 提前 return,只扩正则会完全失效。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
180 会话 649ba617(2026-08-09):模型把一张 deck 截图交给 dashscope,回来的是
400 {"code":"DOWNLOAD_FAILED", "message":"failed to download image, err: Get
\"http://…/restful/data-uri/null/…\": read tcp 10.0.2.60:46976->
10.86.10.104:6000: read: connection reset by peer"}
两端都是厂商自己内网的私有 IP——请求本身没问题,是**他们回取暂存对象时连接被重置**。
但 classify_behavior 把「plain 400」兜进 DEGRADABLE,而主图的 DEGRADABLE 是
re-raise,于是零重试、21 分钟的任务直接判失败。
新增 _TRANSIENT_TRANSPORT_SIGNATURES:当错误里带 Go/POSIX 的传输层失败措辞
(connection reset / refused、broken pipe、i/o timeout、unexpected EOF、
no route to host)时归 RETRYABLE。这些是传输层通用措辞不是厂商错误码,与本模块
既有的扁平签名集(_QUOTA_SIGNATURES / _RATE_LIMIT_SIGNATURES)同一范式,
不引入 per-vendor 分支。
刻意只认传输层失败:404 / 坏 URL / 不支持的格式这类取数失败每次重试都会失败,
必须继续走 DEGRADABLE,不能白烧三次退避。检查点放在 FAIL_FAST 分支之后,所以
欠费/鉴权错误即使正文里恰好带「connection reset」也不会被翻成重试(有守门测试)。
label 层同步:重试耗尽后给「服务繁忙」而不是通用未知卡片。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renaming a conversation looked like a raw text field dropped into the sidebar — a white boxed input, two lucide glyphs, and a row that lost its shape as the options slot emptied. - The row itself becomes the active surface: transparent input, and a 1px #ECECEC hairline + soft drop shadow drawn as shadows (so the box size doesn't shift), matching the chat input's chrome. - Confirm is the only action now: cancel is Esc / blur, and an empty or whitespace-only name silently restores the previous title, so the X button had nothing left to do. Its glyph moves to bisheng-icons. - Opening rename selects the whole title, so typing replaces it. - The options slot unmounts while renaming instead of rendering empty — an empty flex item still cost the row an 8px gap. - Nav widens the scrollport by 1px on the left so the rename ring isn't clipped against the rows' left edge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This pull request introduces several important updates across the backend, configuration, documentation, and error handling for the Bisheng system. The most significant changes include refining skill bundle size limits and error codes, updating container and versioning references, improving permission and file-sharing logic, enhancing error classification for LLM providers, and expanding task execution configuration. Below are the key changes grouped by theme: **Skill Bundle Size Limits and Error Codes** * Raised the skill bundle upload validation to enforce a 10MB upload limit and a new 100MB post-unpack limit, updating documentation and error codes accordingly. Added error code 11059 for unpacked size excess and clarified the distinction from the upload size error (11052). [[1]](diffhunk://#diff-9c8bbce1edd2148e68c276c310b3d24cbd5daa3c831ca9a17749fa85ad33a752L623-R623) [[2]](diffhunk://#diff-fe28570f5992242e5e60cea78e4d9d44c00a4a54279a62f5f7479511e39279eeL162-R164) [[3]](diffhunk://#diff-d9de704b3a76855900ea999b1fcff0f911bb638eea97cfd2166b6d4d0ebca6fbL90-R90) [[4]](diffhunk://#diff-e15dfbd924766b9f180300d6334945fe59ee62dba1e42d6d5719c0e6d6275a38L79-R79) [[5]](diffhunk://#diff-e15dfbd924766b9f180300d6334945fe59ee62dba1e42d6d5719c0e6d6275a38R129-R138) * Updated `SkillDetail` schema to include a `normalized_from` field for tracking original skill names during normalization. **Container Images and Versioning** * Updated all references to backend and frontend container images in `docker-compose.yml` to use the new `v2.6.0-fix2` tag, and bumped the backend Python package version to match. [[1]](diffhunk://#diff-423deb13b7c401b1a7f41ee91c77f722e11d2f317d6a66b546524e8a04cc8b03L84-R84) [[2]](diffhunk://#diff-423deb13b7c401b1a7f41ee91c77f722e11d2f317d6a66b546524e8a04cc8b03L126-R126) [[3]](diffhunk://#diff-423deb13b7c401b1a7f41ee91c77f722e11d2f317d6a66b546524e8a04cc8b03L159-R159) [[4]](diffhunk://#diff-6593331a7fecd32e889ab36d3ef3c414587b13d753f28bf59b5f311c5c8f9daeL7-R7) **Permissions and File Sharing Logic** * Improved async permission checking in workflow services by adding `aget_writeable_app_ids`, and updated frequently used flows to leverage this for determining editable apps. [[1]](diffhunk://#diff-883635cd1cb6d3fef3661d41a4dbd6d320c725aabaed58cacc2af966b9be5ab8R762-R773) [[2]](diffhunk://#diff-883635cd1cb6d3fef3661d41a4dbd6d320c725aabaed58cacc2af966b9be5ab8L1015-R1028) * Enhanced file download endpoint to recognize both session and version-based share links, aligning sharing behavior with other endpoints. **LLM Error Handling Improvements** * Introduced detection and classification of transient transport errors (e.g., connection reset) from LLM providers as retryable and mapped them to `SERVICE_UNAVAILABLE`, improving robustness for upstream fetch failures. [[1]](diffhunk://#diff-2840b8fdc674f1c0751eb30e183009a5a7e226c307d0d5f4aad9174a955d34daR118-R144) [[2]](diffhunk://#diff-2840b8fdc674f1c0751eb30e183009a5a7e226c307d0d5f4aad9174a955d34daR260-R269) [[3]](diffhunk://#diff-2840b8fdc674f1c0751eb30e183009a5a7e226c307d0d5f4aad9174a955d34daR339-R340) [[4]](diffhunk://#diff-2840b8fdc674f1c0751eb30e183009a5a7e226c307d0d5f4aad9174a955d34daR392-R395) **Task Execution and Agent Configuration** * Expanded task execution configuration: increased `max_steps` and introduced `max_model_turns`, `max_model_turns_subagent`, and `soft_landing_turns` for finer control over task and model turn budgets, with clarifying comments in both code and YAML config. [[1]](diffhunk://#diff-cf98cad35174e1a09c8ebaf86a1b07b28ee37acbbc1c5a4917a7d42f2000aafbL369-R393) [[2]](diffhunk://#diff-638898d1db481e05dbfd512b7cea83ef3350c908d7f2354cb5ece469102aa6e8L103-R120) * Updated agent factory documentation to clarify skill execution, deliverables, and tool usage, emphasizing that output files must be written before referencing them in responses. [[1]](diffhunk://#diff-dcc538341258b7b7e983c70fa8cc02e1b784a7e7be55c1ddabe5853f96574d02L120-R130) [[2]](diffhunk://#diff-dcc538341258b7b7e983c70fa8cc02e1b784a7e7be55c1ddabe5853f96574d02L159-R169)
图片索引原本是 Record<cellAddress, imageId[]>,没有 sheet 维度,getCellImage 也只按单元格地址查、不看 activeSheet —— 任何 sheet 的图都会漏到所有 sheet 的 同名单元格。一个首个 sheet 是整页报表截图的文件因此表现为:第一个 sheet 空白, 图跑到第二个 sheet 的 B2。 图归属只能顺 OPC 关系链查出来(workbook.xml 的 r:id → workbook.xml.rels → worksheets/sheetN.xml → sheetN.xml.rels 的 drawing → drawings/drawingM.xml), sheet 顺序和文件编号没有关系,删过 sheet 就会留下空号,不能按序号配对。 同批修掉的其它缺陷: - 只有单元格没有 drawing 的 sheet 被判成「无数据」,纯图片 sheet 永远空白 - cleanData 删空行空列后坐标被压缩,锚点却按原始网格寻址,表中间有空行就错位 - 只认 twoCellAnchor,oneCellAnchor(WPS/报表导出常用)与 absoluteAnchor 的图不显示 - 按 xdr: 字面前缀匹配标签,换个生成器的命名空间前缀就整体失效 - DISPIMG 找不到对应图时返回 images[0],拿第一张图冒充 - cleanData 取值用 falsy 判断,把数字 0 吞成空串 锚在数据区之外的图不再静默消失,与纯图片 sheet 一起按 EMU 换算出的原始尺寸 展示。原文件 643 行已超单文件 600 行上限,顺势拆成 types / sheetUtils / xlsxImages / ImageGallery 四个模块。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…surfaces The member list, the create-page draft and the two "新增授权" dialogs had each grown their own version of the same two things — so the level control looked different depending on where you opened it, and the draft picker was a look-alike of the share dialog rather than the same dialog. - PermissionLevelMenu extracts the list's dropdown (levels, separator, destructive 移除) and the draft editor adopts it, dropping its RelationSelect + separate delete icon button. Removal now always lives inside the menu, never as a button beside it; a row with neither permission renders the plain label at the same width, so the column never jitters. - permissionDialogStyles holds the share dialog's chrome verbatim — shell sizing (80vh card / full-screen under 768px), subject tabs, 包含子部门 toggle, footer captions — and the draft picker consumes it instead of re-approximating it. - Empty authorization lists get their own copy (new key, three languages) instead of borrowing the search-result empty text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This reverts commit 516130dd8258f8ac2e2f68e0e5b3d1ba38b98c9a. 修复留在 3.0 线,2.6 不再跟进——客户后续升级到 3.0 时一并拿到。
- AccessModeSelector rows now behave like the options they are: hover tint, brand-tinted background while checked, and the label kept on one line so only the description truncates. Disabled rows opt out of the hover tint so they still read as unavailable. - That truncated description gets the same hover-only tooltip the member list uses, so TruncatedTooltip moves out of PermissionListTab into its own module. Its polymorphic ref keeps one `any` — now with an inline reason — and the file's suppression entry shrinks. - Both settings pages center their footer actions unconditionally; the knowledge page had centered only in create mode, so the same footer jumped between layouts depending on how you entered it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rating an answer changed only the icon's highlight, which is easy to miss and says nothing about what the rating is for. Every persisted verdict — thumbs-up, or a dislike once its dialog is submitted — now raises a "thanks, we'll use it to improve our answers" toast (new key, all three languages). Un-toggling stays silent: the icon losing its highlight is feedback enough, and a toast there would read as if cancelling had itself been recorded. onLike may now return the request promise, and the two call sites do — so a failed request shows only the interceptor's error toast, never both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dings The knowledge / skill submenus aligned their top edge with the parent menu via a hand-computed alignOffset (measure the trigger, subtract rects, re-measure on resize), and each panel repeated a heading that its own trigger row already carried right next to it. - DropdownMenuSubContent accepts align="center". Radix types SubContent as 'start' | 'end' but hands the value straight to Popper, which does support 'center', so the type is widened rather than the offset hand-computed. The knowledge, org-knowledge and skill panels switch to it and the alignOffset machinery goes away — useSubMenuLayout now only clamps max height against the viewport. - Panel headings drop where the trigger already names the list; the mobile drill panels keep theirs, since there the heading IS the back-navigation label. The knowledge pill's two variants collapse into one branch with a mobile-only heading (the narrow toolbar can render its trigger icon-only). - Skill panels grow to 280×440 so the list isn't a letterbox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…itles The two hand-drawn knowledge glyphs (BookOpen / Books) predate the matching bisheng-icons entries. Every call site — hub nav tab, the attachment bar's KB card, the task-mode context chips — switches to Outlined.Book / Outlined.Books, and the local SVG components are deleted (ChatKnowledge only carried dead imports). Sidebar section titles drop from bold to semibold, matching the weight the rest of the sidebar chrome uses. Suppressions: pruned the entries the deleted icons left behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The copy link was underlined on hover and swapped its own label to "copied" for two seconds. Both read as emphasis the sentence does not want, and a label that changes under the cursor is easy to miss on the one screen a user is likely to be screenshotting rather than reading. The link now never underlines and never changes text; ErrorPage fires a new `onCopied` callback instead, and the client raises its usual toast (existing `com_ui_copied_to_clipboard` key, all three languages). The package still carries no i18n and no toast of its own. `labels.copied` and the now-dead `com_error_page.copied` key are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## What 简要描述做了什么改动。 ## Why 为什么需要这个改动? ## How 实现方式、设计决策(如有)。 ## Test - [ ] 本地测试通过 - [ ] 114 测试服务器验证通过 ## Related - Issue/ticket:
Rename is entered from a Radix dropdown item, and Radix restores focus to the menu trigger as it unmounts — after our synchronous focus() call. The input therefore never held focus, never fired blur, and clicking away left the field open with the edit unsaved. Two halves: re-assert focus once that restore has run (in the existing selection timeout), and commit from a capture-phase pointerdown listener so an outside click saves even when blur never fires. Since an outside click now reaches the commit twice — the listener, then the input's own blur — a ref mirrors isRenaming so the second one is a no-op instead of a duplicate onRename. Tests cover both paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening a row's options menu or its rename input moves the pointer off the row — into the portaled menu — so the hover fill dropped away and the row you were acting on stopped looking involved. Both sidebars now pin the hover fill for as long as the row owns that UI. It stays the hover fill, never the active row's: this row is not the open conversation. On coarse pointers it applies too, where it reads as "this row owns the menu" rather than as a hover affordance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "+" menu, knowledge pill, skill panel and the model / group selects had each drifted: 8px vs 16px radius, per-menu padding, rows sized by padding instead of height, and icon greys ranging over #999 / slate-600. - Menus settle on rounded-2xl, 12px padding, 8px gap and 32px rows with 8px radius. Select grows a viewportClassName so its scroll viewport can carry that padding instead of the outer panel (which would clip the scrollbar). - Resting icons all use #4E5969; active stays brand. The upload item drops its baked-colour link.svg for Outlined.Attachment so it can follow that tint. - The "+" panel aligns to the input box's left edge rather than its own trigger, which sits 13px inside it. The knowledge pill keeps trigger alignment — mid-toolbar, where the input edge means nothing. - Skill rows carry selection themselves (brand tint + trailing check) now that the leading checkbox is gone; name and description each clamp to one line and move the full text into a tooltip, but only when measurement says they actually overflow. Empty search gets the illustration instead of bare text. - The knowledge panel takes its own 320px cap (skills/org sit at 440) so it still opens downward more often, and the hand-rolled useSubMenuLayout measuring goes away — align="center" plus Radix's own collision handling already place these panels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AiMessageBubble carried its own extension → bisheng-icon table, a copy of what FileIcon already knows. FileIcon exports getFileTypeIcon and the bubble consumes it, so a new file type is registered once. Also deletes FileListRow and the knowledge mock: neither has had a call site for a while (the file list renders through FileTable / FileCard). Suppressions shrink accordingly. The share dialog picks up the icon import while it is here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The supported-formats hint was the only tooltip here still hand-skinned — white panel, no arrow, its own text colour — so it read as a different kind of popup from every other tooltip on the page. It drops back to the shared TooltipContent defaults and moves to `side="top"`, keeping only the width cap and z-index it actually needs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Twenty-one of the fifty-three standing violations, fixed rather than suppressed: - useWebsocket: two switch cases get block scopes, so their `const`s stop leaking into sibling cases (9 × no-case-declarations). - FilePreview: both @ts-ignore comments were dead — __APP_ENV__ has had a global declaration for a while — so they just go. - useFileUpload: the `_raw` duplicate-row passthrough is now modelled on KnowledgeFile as RawSpaceChild instead of being cast in and out at three call sites; upload rejections narrow through one readUploadError helper rather than `err: any` plus two `(err as any)` reads; and the three `as any` on showToast were never needed — TShowToast has always accepted `severity`. Two things the removed `any`s had been hiding: i18next's t() rejects a runtime-assembled `api_errors.{code}` key (it is not in the generated key union — exists() is the real guard, now stated as such), and its interpolation values must be scalars, so errorData is filtered to them. Suppressions pruned accordingly; typecheck still clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The four params/body objects these endpoints assemble are written to and handed straight to http — nothing reads a property back — so they carry Record<string, unknown> instead of Record<string, any>. Suppression budget shrinks with them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # src/frontend/client/eslint-suppressions.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
简要描述做了什么改动。
Why
为什么需要这个改动?
How
实现方式、设计决策(如有)。
Test
Related