Repository navigation
Conversation
- Add bounded retries and structured refresh outcomes for models.dev downloads. - Replace the in-memory catalog projection immediately after a successful refresh. - Emit catalog update events and forward them through peer UI transport. - Reload catalog data in ModelSelector and AI model settings. - Add service, runtime, transport, and frontend regression tests.
Discover bounded Claude Code user and project rules with paths front matter, then activate matching instructions after successful workspace reads. Preserve source identity through session history and drop conditional reminders during compaction so later reads can reload current content.
Allow state-based shell-turn tests to tolerate loaded Windows runners while preserving immediate completion and bounded deadlock detection.
- extend remote model selection with present-nullable reasoning presets - validate presets against the resolved model catalog before atomic updates - synchronize canonical model and preset selections through catalog polling - add mobile reasoning controls with backward-compatible capability gating - cover protocol compatibility, selection forwarding, and catalog invalidation
Register the coordinator local-command persistence port in the narrow session runtime so usage reports can be saved through the desktop session API. Add a regression test for the runtime assembly.
- keep usage report turns provisional until persistence succeeds - return authoritative turn IDs and storage indexes from the runtime port - reconcile the session catalog and loaded history ranges after persistence - exclude provisional reports from turn rails and history tail boundaries - remove failed provisional reports and prevent frontend-only metadata persistence - document the in-memory and partial-history semantics of dialogTurns - add regression coverage for partial-history pagination and report persistence
- add recursive validation for preset IDs, budgets, sequences, and request patches - validate default presets against the projected models.dev catalog - enforce validation across config loading, updates, resets, and imports - align duplicate preset resolution between projection and runtime lookup - add regression coverage for invalid schemas and configuration rollback
Remove the temporary Tao Git patch now that 0.36.0 includes the Windows keyboard and IME deadlock fix. Advance the patched Tauri runtime revision to its Tao 0.36 and Wry 0.56 integration while retaining the Windows child-webview focus fix.
- Make the leading-slash command picker follow the current composer text exclusively. - Limit inline skill-picker synchronization to `$` and non-leading `/` triggers. - Prevent stale inline-trigger state from reopening the command picker after `/` is deleted or the picker is dismissed. - Add regression coverage for leading-slash trigger classification.
- preserve the upstream MIT license and add third-party attribution - record source revision, retrieval metadata, transformation rules, and hashes - add reproducible snapshot update and offline validation commands - bundle models.dev notices with Desktop and CLI releases - enforce provenance validation through repository hygiene checks
- Restore protected nested runtime fields after every JSON Merge Patch. - Prevent null, scalar, and array replacements from removing generationConfig.maxOutputTokens. - Add regression coverage for selected presets, model defaults, and preset sequences. - Verify with adapter tests and workspace cargo check.
Keep TUI lineage reads nonblocking, move settlement validation to the Runtime owner, and make shared IPC reads supersedable and full duplex. Preserve exact cancellation outcomes and fix the confirmed Windows Dispatch test and worktree path portability issues.
- validate reasoning presets against the resolved model catalog - persist unavailable presets as Auto during create, update, restore, and catalog reconciliation - keep turn-time fallback as a final safety net - synchronize automatic preset clearing across Web UI, CLI, and remote projections - add focused persistence, event, and frontend state tests
Implements the Skin marketplace across desktop, web, backend, agent interactions, deployment assets, shared GitHub authentication, submissions, and review workflows.
- store reasoning presets in controller outbound dispatch records - project pending and applied presets through target job listings - restore presets in Web observers while preserving legacy compatibility - cover explicit auto, follow-up adoption, and restart recovery
- merge tested adapter fallbacks between models.dev and model configuration - restrict inferred presets to trusted endpoints and known model families - keep unknown models and custom gateways fail closed - preserve models.dev authority and model-config override behavior - verify fallback presets resolve through runtime catalog and client application
- replace the Windows cache atomically without removing the active snapshot - support BITFUN_MODELS_DEV_PATH as a local runtime catalog source - preserve the last valid cache when local catalog validation fails - add focused tests and register Windows dependency ownership
Restore MiniApp/Skin shared GitHub marketplace sessions, accept the canonical OAuth returnTo parameter while retaining legacy compatibility, and document the coordinated deployment contract.
* feat(relay): deploy from published multi-arch image * fix(relay): prefer verified China image route * test(relay): smoke published image platforms * fix(ci): isolate Relay platform smoke pulls
* fix(skin-market): add desktop and website links * fix(appearance): register manual submission surface
…id-model-response fix(agent): recover transient model routing and goal worktrees
feat(input): show paths in file mentions
…rrors fix(ai): retry every provider error
Reattaching on every start turned out to be too eager. The browser only keeps its approval grant while it stays running, so whenever the browser had restarted since the last connection, BitFun's startup reattach put an approval dialog in front of the user before they had asked for the browser at all. Gate it behind ai.browser_control_auto_connect_on_startup, off by default, alongside the default CDP row it applies to. The setting's description says what turning it on does, including the dialog after a browser restart, and what stays true when it is off: BitFun attaches the first time an agent needs the browser.
…ect-opt-in fix(browser): make startup reattach opt-in
The root agent guide only warned about remote SSH workspaces, so changes routinely shipped complete for the local desktop and incomplete for the other remote paths: relay-driven remote control (mobile web and IM bots), Peer Device Mode, and detached Dispatch jobs. Replace the "Remote compatibility" section with "Remote scenarios": - A table naming all four scenarios with their design entry points. - Cross-cutting rules: design the remote path with the feature, degrade loudly instead of falling back to the local host, keep blocking prompts answerable from a distance, survive disconnect, and treat remote workspace paths as POSIX on every client OS. - Per-scenario obligations pointing at the real enforcement points: the remote-workspace policy registry, the RemoteCommand wire protocol and bot command router, the three peer deny lists that must stay in sync, and the dispatch observer/capability contract. AGENTS-CN.md is updated to match.
docs(agents): expand remote scenario guardrails
- refresh reasoning projections while editing explicit models.dev bindings - warn when the active API format cannot reliably apply catalog presets - add combined provider and model search with ranked multi-keyword matching - streamline custom preset naming and hide internal preset IDs
fix(settings): streamline copy and align controls
Remote scenarios routinely put two different BitFun versions on one connection, and users upgrade in place, but the agent guide had no rule covering either. Add an "Upgrade compatibility" global rule next to the remote scenarios section, matching what the existing remote-workspace and transport designs already practice: - persisted shapes stay tolerant, defaulted, and never repurposed; - unparseable data degrades instead of being deleted or reset; - cross-version boundaries advertise and check a capability rather than assuming behavior from a package version; - a rename is a migration, including the data it references; - upgrade coverage means legacy deserialization and old-payload round trips, not only the current shape. AGENTS-CN.md is updated to match.
The Web UI transport adapter denies 10 controller-owned commands that neither peer host refuses: the nine `speech_*` capture/model commands and `dispatch_continue`. The controller-side list is only an optimization — an older controller build, or any non-Web-UI controller, still reaches a peer host over HostInvoke, and the host would then run them. That means microphone capture sessions and speech model file management executing on the peer instead of the machine the user speaks at, and a dispatch control-plane verb running against the peer's outbound observer records and SSH credentials rather than the controller's. Every sibling dispatch verb is already denied on all three lists; `dispatch_continue` was simply missed. Deny both families on the desktop and CLI peer hosts, and add contract tests covering them. The README already required these three lists to stay aligned, but nothing enforced it. Add a peer command policy check to the core boundary checker that fails when a controller-denied command is missing from either host list, so the boundary cannot drift again. The check is one-way: a host denying more than the controller stays allowed. The CLI's four pre-handled control-plane commands are declared exceptions, and a stale exception is itself reported. Verified: cargo test -p bitfun-cli --bins peer_host, cargo test -p bitfun-desktop --lib peer_host_invoke / remote_workspace_policy, and the new check both passing on the fix and reporting all 10 commands without it.
Reconnecting to a peer device left the chat blank until the user clicked a session in the sidebar. Three gaps in `initializeWorkspace`, all reachable only in Peer Device Mode because `clearAllSessionsForPeerSwitch` is the one place that bumps the store surface generation: 1. A metadata page loaded across a surface-generation bump is discarded by `processPersistedSessionMetadataList`, but the page is still returned with its sessions. The caller then sees "history exists" over an empty store. Reload once against the settled generation. 2. History was only restored on the auto-select path, so a session that was already active but still metadata-only was never hydrated. The breadcrumb and turn rail render from the catalog while the message area stays empty — the reported symptom. Restore history for an active historical session too. 3. When metadata claimed sessions but none were selectable, initialize returned true while selecting nothing. The caller reads true as "do not create a session", so the surface ended up with neither. Report no history so the caller creates against the live workspace instead. No persisted shape, command, or protocol changes; an older peer host is unaffected. Verified: all three tests fail without the fix and pass with it; the 8 existing FlowChatManager cases are unchanged; 252 tests across src/flow_chat/services pass; pnpm run type-check:web clean.
…r-retries fix(agent): unify live model retry budget
`browser.wait` read only `duration_ms`, so the very plausible
`{ "ms": 1800000 }` was dropped and the call fell through to a branch that
returned `{ success: true }` instantly. An agent asked to pause 30 minutes
got "Wait completed" back in milliseconds and moved straight on. The
action documented no parameters at all, so the model had to guess the key,
and even a correct guess was silently capped at 30 seconds.
- Accept the spellings models emit: `duration_ms` / `ms` / `wait_ms` /
`sleep_ms`, plus `seconds` / `secs` variants, numeric strings included.
- Reject a `wait` carrying neither duration nor condition with
INVALID_PARAMS instead of reporting a success that never waited.
- Raise the cap to 60 minutes, in step with AgentWait's MAX_TIMEOUT_MS, and
report `ms` / `requested_ms` / `clamped` so a shortened wait says so.
- Race the sleep against the turn's cancellation token, and stop
`call_impl` from folding Cancelled into an `ok: false` envelope — a stop
during a long pause must not wait out the pause, nor look like a tool
error the model tries to recover from.
- Serve duration waits before session resolution: a pure pause touches no
page, and agents pace themselves long before they open a browser.
- Resolve `{ condition, timeout_ms }`: the condition always wins and any
duration bounds it, rather than sleeping and never looking at the page.
Condition waits keep their previous 15s default, now configurable.
- Document all of it in the tool description, and point repeating schedules
at the Cron tool, which ends the turn instead of pinning it open.
Asked to sweep a set of channels every 30 minutes, a Cowork-mode agent replied that it had no cron tool and fell back to chaining long `wait` calls. It was telling the truth: `Cron` was in Claw's tool list and nowhere else, so in Cowork, Team, DeepResearch, and the four shared coding modes the agent could not see it at all. Deferred exposure is not the cause — deferred tools are advertised by name — the tool simply was not in those modes' `default_tools`. ControlHub's `wait` documentation now tells the agent to schedule repeating work with Cron instead of holding the turn open, and ControlHub ships in all of those modes, so the guidance pointed at a tool the agent did not have. - Add `Cron` to Cowork, Team, DeepResearch, and `shared_coding_mode_tools` (agentic / debug / multitask / plan), so it is available everywhere ControlHub is. - Cover the pairing with an invariant test over the built-in modes; it caught the shared coding baseline, which the first pass had missed. - Say in ControlHub's description what to do if Cron is genuinely absent, rather than leaving a chain of long waits as the silent fallback. Scheduling stays behind the normal permission gate — Cron is not read-only and emits a `custom_tool` intent — so this widens what the agent can propose, not what it can do unattended.
Asked to pace itself with a cron job, the agent created one and then kept working for another half hour before stopping on its own. Nothing was broken: `Cron add` returns "Created scheduled job ..." and nothing more, so the call reads as an ordinary success, and no tool can end a turn — only the model stopping ends it. With the loop it just handed to the scheduler still in its head, it drives that loop itself. That is worse than untidy. A scheduled run is submitted as a queued dialog turn at low priority, so it is never run in parallel with the turn already in flight; a round that outlives the interval simply delays the trigger it is racing. The round in question took 32m against a 30m interval, which turns "every 30 minutes" into back-to-back rounds. - Say it in the `add` result, but only when the job actually takes over this turn's cadence: it repeats, and it targets this session. A one-shot reminder, or a job scheduled for some other session, must not cut the current turn short. - Say it in the tool description too, so the model knows before it commits to a plan rather than after — including that a run firing into a busy session is queued, so the interval wants to be longer than a round. - Drop the claim, added with the browser.wait fix, that Cron "ends the turn and re-invokes you". It does not, and a model that believed it would keep the turn open waiting for an end that never comes.
There was a problem hiding this comment.
建议暂缓合并。这个 PR 对进程启动、IPC 和关闭流程写得比较细,但几个更上层的边界还需要先定清楚,否则实现后很容易出现两套插件运行机制并存、OpenCode 接口反向侵入 BitFun 核心模块,以及插件权限和崩溃恢复无法闭环的问题。
以下意见只保留与当前设计直接相关的架构问题。
[P1] 不要绕过现有插件运行入口,另建第二套
问题
bitfun-opencode-ext-host-ipc-design.md:118-140,743-765 让 Core 和 Session 直接持有、调用 HostClient;opencode-plugin-client-route-adapter-design.md:192-209 又准备在 Core/Assembly 增加 PluginHostBackendBridge 和实例状态。
但仓库已经有统一的 PluginRuntimeClient。如果 Core 可以直接调用 Host,就会同时存在两条插件执行路径。
风险
两条路径会分别处理插件启停、超时、取消、崩溃恢复、权限检查和结果发布。Desktop、CLI、Remote 后续也可能各走各的入口,最终很难判断哪一套状态才是真的。
建议
产品模块只能调用 PluginRuntimeClient:
产品功能模块
-> PluginRuntimeClient
-> OpenCode 适配层
-> 进程服务
-> 一个 Plugin Host
-> 一个或多个插件执行进程
Assembly 只负责选择和注入实现;OpenCode 格式转换留在 OpenCode 适配层;Host 及其下属插件进程的启动、连接和回收留在 services。插件执行进程返回的工具、Hook 等内容还要交给真正的功能模块校验后再生效,不能由 Host 或 Assembly 直接注册。
完成标准
- Core/Assembly 不直接持有
HostClient或 Host 实例表。 - Desktop、CLI、Remote 都只能通过同一个插件运行入口调用。
- 插件启停、权限和恢复状态各自只有一个明确负责人。
[P1] 不要把 OpenCode 的整套 HTTP 接口变成 BitFun 内部总入口
问题
opencode-plugin-client-route-adapter-design.md:54-69,192-209,340-403 计划通过一个 backend.http.request,把 OpenCode SDK 请求分发到 Workspace、Session、文件、终端、Git、MCP、LSP、配置和模型供应商等大量 BitFun 模块。
文档虽然说这不是完整 OpenCode Server,但如果 Core 需要理解并分发这么多 OpenCode HTTP 路径,实际上仍然是在 Core 中建立一层“精简版 OpenCode Server”。
风险
OpenCode 一旦改接口,BitFun 多个核心模块都可能跟着改;其他插件生态也可能照此再建一套总入口。长期会出现多套外部协议直接穿透 Core,各功能模块原有的权限、取消和状态管理被绕开。
建议
只实现真实插件已经使用、并且有固定样例验证的 Client 接口。HTTP method、path 和 OpenCode DTO 都留在 OpenCode 适配层;适配层再调用各功能模块已有的窄接口,不要定义一个覆盖所有服务的通用 BackendCapability。
这里的 1.17.18 只需要被说明为“当前内嵌 SDK 和路由表的参考版本”。现有其他文档使用 1.18.4/1.18.9,应说明它们分别是历史审计快照还是当前实现依据,但没有必要为了尚未提出的多版本兼容设计复杂的版本协商。
完成标准
- OpenCode 路由变化不会迫使无关 Core 模块修改公共接口。
- 每条开放路由都有真实插件样例和端到端测试。
- 未验证路由明确返回不支持,而不是先铺完整 SDK 路由表。
- 文档明确
1.17.18的作用范围,并处理与其他参考版本的表述冲突。
[P1] 配置里写了插件,不等于已经允许执行
问题
bitfun-opencode-ext-host-ipc-design.md:145-164,497-523,676-739 把全局 app.json.plugin 非空作为启动 Host、导入插件和打开工作区插件实例的主要条件。
配置只能说明“用户或项目声明了这个插件”,不能直接证明“当前这份代码已经被允许在这个位置执行”。
风险
用户确认之后,本地文件或 npm 包内容可能已经变化;应用重启时也可能从同一路径加载到不同内容。远程工作区还可能错误沿用本机的确认结果。
建议
把两个步骤明确分开:
- 从配置中发现插件;
- 在执行前确认插件来源、当前实际内容、运行位置和权限仍然符合用户之前的决定。
Host 只能接收已经通过第二步检查的插件清单,不能自己根据原始 app.json 判断是否可以执行。重启、插件更新、切换到远程环境时都要重新检查当前内容和执行位置。
完成标准
- 没有有效执行许可时,不能启动 Host 或导入插件代码。
- 插件内容、运行位置或权限变化后,旧许可不能继续使用。
- 本地和远程环境分别判断,不允许远程失败后偷偷回到本机执行。
[P1] 明确 Host 和插件执行进程的关系,不要把两层混成一层
问题
一个 backend 对应一个 Host 是合理的:它可以统一管理连接、插件清单、请求转发、日志和关闭流程,也能避免每个工作区重复创建管理进程。
但当前文档没有把“管理 Host”和“真正运行插件代码的进程”分开。bitfun-opencode-ext-host-ipc-design.md:89-116,442-486,676-739 把一个 backend 对应到一个选定的 Node/Bun Host,并把多个插件声明直接交给这个 Host;现有通用设计也把 Plugin Host 定义成“直接加载并运行第三方插件代码的进程”。这与“一个 Host 管理多个插件执行进程”是两种不同的进程结构。
风险
如果按当前文字实现,所有插件仍会在唯一 Host 进程内执行。一个插件的死循环、内存耗尽或主动退出就会带走全部插件;Node/Bun 也只能在整个 backend 级统一选择。
反过来,如果实现人员自行在 Host 下增加多个插件进程,却没有文档规定谁启动、停止和回收它们,又会产生无人负责的子进程和不一致的恢复流程。
建议
明确采用下面的两层结构:
BitFun backend
-> 一个 Plugin Host:统一管理连接、插件清单、请求转发、日志和关闭
-> 多个插件执行进程:真正导入并运行第三方插件代码
一个 Host 管理多个执行进程即可,不必现在写死“一个插件一个进程”。运行环境相同、可以安全共享的插件可以共用执行进程;需要不同 Node/Bun 环境、远程位置或隔离条件时再拆开。
services 仍应掌握完整进程树,确保 backend 或 Host 退出时可以回收所有插件进程。Node/Bun 的选择如果是插件代码的运行要求,应放在插件执行进程这一层,而不是由唯一 Host 全局决定。
完成标准
- 文档明确 Host 是管理进程,插件代码由下层执行进程运行。
- 一个插件执行进程崩溃时,只撤下受影响插件,不必让所有插件失效。
- Host 崩溃时,Rust 将其管理的全部插件进程一起标记失效并统一恢复。
- backend 退出时,所有插件进程都能由受监督的进程树完整回收。
[P1] 如果要做“按插件授权”,请求里就必须知道是哪个插件发起的
问题
opencode-plugin-client-route-adapter-design.md:78-85 的请求只有 instance、请求编号、HTTP 方法和路径;:303-315 保存的也只是工作区和一份统一权限,没有插件身份。
但同一个工作区实例会加载多个插件。当前协议无法判断某个文件、终端或 Session 请求究竟来自哪一个插件。
风险
如果不同插件应有不同权限,一个插件就可能使用另一个插件获得的权限;日志也无法回答“是谁执行了这次操作”,撤销单个插件的权限同样无法立即生效。
建议
Host 给每个插件创建独立的 Client,请求由 Host 自动附带真实插件身份、当前插件内容版本和当前权限版本,不能让插件自己通过 header 声明身份。Rust 收到请求后,再根据该身份检查权限。
如果产品决定所有插件完全共享权限,也可以不做上述隔离,但文档就不应再承诺“按插件检查权限和审计”。两种模型需要明确选一个。
完成标准
- 能确认每个请求来自哪个插件。
- 两个插件可以拥有不同权限和独立审计记录。
- 禁用一个插件后,它不能继续借用旧请求或旧权限调用后端。
[P1] Host 崩溃后,旧能力必须整体撤下,再整体恢复
问题
bitfun-opencode-ext-host-ipc-design.md:813-820 规定 Host 丢失后实例失效,并在后续工作区操作时重新启动、重新打开实例,但没有说明旧的工具和 Hook 何时撤下、多个工作区是否一起恢复,以及恢复一半失败时产品应处于什么状态。
风险
可能出现旧工具仍显示可用但实际 Host 已经死亡,或者部分工作区使用新 Host、部分仍保留旧状态。对于删除、更新、执行工具等操作,如果请求已经送出但响应在崩溃时丢失,简单返回“连接断开”还可能诱导调用方重复执行。
建议
Host 崩溃后应按一个完整流程处理:先停止接收新调用,取消或等待旧调用,撤下该 Host 提供的全部工具和 Hook,启动新 Host,重新加载所有仍在使用的工作区,全部校验成功后再一次性恢复可用状态。中间任一步失败,就保持不可用,不能留下半新半旧状态。
对于已经发出但不知道是否执行成功的写操作,应返回清楚的“结果未知”,禁止自动重试,由真正保存状态的模块重新查询结果。
完成标准
- 单个插件执行进程崩溃时只影响对应插件;Host 崩溃时,其管理的全部插件一起失效、一起恢复。
- 恢复完成前旧工具和 Hook 不再可见,也不接收新调用。
- 任一工作区恢复失败时不会发布一半的新状态。
- 写操作丢失响应时不会自动重复执行。
以上问题都来自当前文档已经提出的生产路径,不依赖 OpenCode v2 或假设未来一定支持多版本。建议先把这些边界修正,再继续补充路由和进程实现细节。
|
Thank you for your contribution. The If your changes are still needed, please reapply them on a fresh branch based on the new We apologize for the disruption and appreciate your understanding. |
Summary
Type and Areas
Type: docs
Areas: Rust core architecture, CLI/plugin host, OpenCode extension compatibility
Motivation / Impact
本 PR 将插件 Host 相关设计文档与运行时代码拆分,便于独立评审和后续维护。
文档覆盖以下内容:
input.client.*到backend.http.request的调用链路。当前文档基线以
@opencode-ai/sdk@1.17.18为准:pty.connect()、WebSocket、事件流、认证、TUI 等能力:不在本阶段适配范围内。对应运行时代码实现位于配套 PR:
Verification
git diff --check origin/main...HEADReviewer Notes
docs/architecture/extensions/bitfun-opencode-ext-host-ipc-design.mddocs/architecture/extensions/opencode-plugin-client-route-adapter-design.mdChecklist