Releases: dragonTalon/dsh-browser-assistant
Release list
bridge-dsh v0.3.0
新功能 · New features
-
会话权限档位闸门 · Session permission-tier gate — 浏览器写操作的授权不再是「一律人工审批」,而是会话级权限档位:桥接从会话自身的持久旋钮事件(
permission/preset+sandbox/mode+approval/policy)折叠出当前档位,并在工具调用到达扩展之前闸门每次调用——仅可查看直接拒绝改页面与开网站且不发帧,工作区内修改放行并下发ask(无应答即拒绝、超时即拒绝),完全权限放行并下发auto(不产生审批请求)。新增permission.setRPC(经 dsh 的/permission <preset>切换)与session/permission事件(仅在档位实际变化时推送);档位下降时桥接用tool.cancel撤回该会话在途的浏览器调用,含正等待确认的那些。求解结果是互不折算的三态:已求解 / 无档位能力(部署未公布预设表,退化为档位之前的读写两态)/ 求解失败(以permission-tier-unresolved显式失败,不产生审批请求,也 MUST NOT 回退到任何档位)。档位名以部署公布的预设表为准(不硬编码白名单),custom按最严档位处理;投影只用于获知预设表并与折叠结果交叉核对,分歧时以更严一方判定并留痕。授权不可伪造:扩展上报的任何「已确认」声明不参与判定。
Browser write authorization is no longer a flat "always confirm" — it is a session-level permission tier. The bridge folds the session's own durable knob events (permission/preset+sandbox/mode+approval/policy) into the current tier and gates every call before it reaches the extension:read-onlyrefuses page mutation and navigation outright and writes no frame,workspace-writepasses the call withask(no answer, or a timeout, means refusal),danger-full-accesspasses it withauto(no approval request at all). Newpermission.setRPC (switching via dsh's/permission <preset>) andsession/permissionevent (emitted only on a real change); on a downgrade the bridge withdraws that session's in-flight browser calls withtool.cancel, including ones already waiting on a confirmation. Solving yields three mutually non-collapsing outcomes: solved / no tier capability (the deployment publishes no preset table; the pre-tier read/write behavior applies) / solve failed (fails explicitly withpermission-tier-unresolved, produces no approval request, and MUST NOT fall back to any tier). Tier names come from the deployment's advertised preset table (never a hardcoded whitelist),customis treated as the strictest tier, and the projection is read only for the preset table plus a cross-check against the fold — a disagreement gates on the stricter side and leaves a trace. Authorization cannot be forged: nothing the extension claims participates in the decision. -
浏览器工具注册表:工具语义的唯一事实来源 · Shared browser-tool registry: one source of truth for tool semantics —
@dsh-browser/protocol新增browser-tools.ts:12 个工具的名称与语义分类(read/observe/mutate/navigate、是否附带页面 delta、是否导航候选)由这一份注册表唯一声明,桥接档位闸门与扩展的审批判定、delta 附带、导航快照策略全部从它派生。注册表无法识别的工具名在桥接层以稳定错误码unknown-tool失败,且不产生tool.call帧——动作不得到达扩展或页面,也不产生审批请求。pnpm check:tool-registry断言全部消费点与注册表一致,并用「删掉一条注册项」的协议 shim 复现漂移构建,证明拒绝发生在任何帧之前。
@dsh-browser/protocolgainedbrowser-tools.ts: the 12 tools' names and semantic classifications (read/observe/mutate/navigate, page-delta attachment, navigation candidacy) are declared in exactly one registry, and the bridge's tier gate plus the extension's approval judgement, delta attachment and navigation snapshot policy all derive from it. A name the registry does not recognize fails at the bridge with the stable codeunknown-tooland writes notool.callframe — the action never reaches the extension or the page and produces no approval request.pnpm check:tool-registryasserts that every consumer agrees with the registry and re-bundles with a protocol shim missing one descriptor to prove the refusal happens before any frame. -
适配层只做形状翻译,业务逻辑与 dsh 版本解耦 · The adapter only translates shapes; business logic is decoupled from the dsh version — 新增
host-streams.ts定义四个传输原语,并把事件代(event-generation.ts)、历史压缩行展开(history-expand.ts)、工作区分组(session-grouping.ts含瞬时失败重试)从remote-host-api.ts中拆出。dsh 版本演进现在只重写适配层的形状翻译,跨代游标、断连回补、waterfall 归属、分组注册等业务模块零改动,并能对 fake transport 离线断言(pnpm check:adapter-seam,16 项)。
Newhost-streams.tsdefines four transport primitives, and event generation (event-generation.ts), compacted-history expansion (history-expand.ts) and workspace grouping (session-grouping.ts, including the transient-failure retry) were extracted out ofremote-host-api.ts. A dsh version change now rewrites only the adapter's shape translation, leaving cross-generation cursors, disconnect backfill, waterfall ownership and grouping registration untouched — and those modules are asserted offline against a fake transport (16 assertions inpnpm check:adapter-seam). -
一条命令的离线套件 + 拒绝在失败时打 tag 的发布门禁 · A one-command offline suite plus a release gate that refuses to tag on failure — 新增
scripts/run-tests.mjs作为唯一入口:pnpm test= typecheck + 9 个离线行为校验,pnpm test:e2e追加两条实机检查;scripts/tag-release.sh先核对版本号与对应包的package.json(扩展还要核对manifest.json)一致,跑完门禁才创建并推送 tag,任何一步失败都不留下 tag。同时清除仓库里的机器相关绝对路径:tsconfig.base.json的typeRoots与两个build.sh的 deepseek-harness 回退分支全部删除,dsh 宿主类型改由scripts/link-dsh-types.sh软链进包内;依赖一律来自 workspace 安装(pnpm install --frozen-lockfile --offline走本地 store 即可恢复)。pnpm check:permission(43 项)与pnpm check:tool-registry把档位规则与注册表漂移钉住。离线校验本身也不再依赖本机环境:打包时把只在 dsh 安装里存在的@deepseek-ai/*宿主包替换为 stub,构建产物断言仅在产物存在时执行(干净 checkout 上如实 SKIP 而不是失败),ws声明为根 devDependency——干净 checkout 与 CI 上pnpm test都能跑完全部 9 项。
Newscripts/run-tests.mjsis the single entry point:pnpm test= typecheck + 9 offline behavioral checks,pnpm test:e2eappends the two live checks.scripts/tag-release.shverifies the version against the package'spackage.json(and the extension'smanifest.json), runs the gate, and only then creates and pushes the tag — a failing step leaves no tag behind. Machine-specific absolute paths are gone from the repo: thetypeRootsentry intsconfig.base.jsonand the deepseek-harness fallbacks in bothbuild.shscripts were deleted, dsh host types are linked into the package byscripts/link-dsh-types.sh, and dependencies come from the workspace install (pnpm install --frozen-lockfile --offlinerestores them from the local store).pnpm check:permission(43 assertions) andpnpm check:tool-registrypin the tier rules and the registry drift. The offline checks no longer depend on this machine either: the@deepseek-ai/*host packages, which exist only inside a dsh installation, are replaced by stubs at bundle time, the built-artifact assertions run only when an artifact is present (a clean checkout reports an honest SKIP instead of failing), andwsis declared as a root devDependency — so all 9 checks run on a clean checkout and on CI.
排查能力 · Diagnostics
-
分组的每一步都有痕迹 · Every grouping decision leaves a trace — 会话分组是「静默增强」:注册失败只让会话落回「未分组」,此前除了一行 warn 之外看不出桥接是否尝试过注册、是否复用了缓存身份、或是否根本没走到这一步。现在启用分组时会打印一行
会话分组已启用…,之后每次session.create都有一条轨迹:正在把 <路径> 注册为 dsh 工作区…→工作区已解析 workspaceId=…(成功)或sessionWorkspace … 注册失败(<code>: <message>)(失败);自带workspaceId/cwd的请求报告「原样转发」,调用方已取消的请求报告「跳过注册、不会分组」。离线契约校验从 16 项扩到 24 项,把「轨迹必须能区分这三种结局」也钉住。
Session grouping is a silent enhancement: a failed registration only drops the Session into "Ungrouped", and beyond a single warn line nothing showed whether the bridge tried to register, reused a cached identity, or never reached that code at all. Enabling grouping now logs会话分组已启用…at mount, and everysession.createleaves a trace:正在把 <path> 注册为 dsh 工作区…→工作区已解析 workspaceId=…(success) orsessionWorkspace … 注册失败(<code>: <message>)(failure); a request carrying its ownworkspaceId/cwdreports that it was forwarded untouched, and an already-cancelled caller reports that registration was skipped. The offline contract check grew from 16 to 24 assertions, pinning that those three outcomes are distinguishable in the trace. -
check:grouping:status:只读排查脚本 ·check:grouping:status: a read-only triage script — 对照 dsh 工作区注册表里的成员账与配置目录下真实存在的会话文件,列出没有归入分组的会话;不需要 dsh、不需要 Chrome、不产生任何 RPC。用来回答「会话在 dsh 里但不在分组下」时,究竟是注册没发生,还是那次会话早于分组能力。
Diffs the dsh Workspace registry's membership against the Session files that actually exist under the configured directory, listing the ones that never landed in the group — no dsh, no Chrome, no RPC. It answers whether a "session exists but is not grouped" report means registration never happened, or that the Session simply predates the feature.
修复 · Fixes
- 档位真正生效:读的是发布视图,不是宿主 fold state · Tiers actually apply now: the bridge reads the published view, not the unit's fold state — 桥接层从
sessionProjections.stateOf(session, 'permissions')读档位,但那返回的是该投影单元的宿主状态(preset/sandbox/approval/seeded),而档位视图(options/currentValue)是单元view()产出、经viewSchema校验后才交给客户端的那一份。字段名不同,于是resolvePermission永远得到undefined、判定为「本部署无档位能力」,而扩展侧对无策略的调用按 fail-closed 当作ask。后果是档位整体失效且没有任何报错:选「完全权限」照样每次写操作弹确认;选「仅可查看」也不会在桥接层拒绝改页/导航(只剩扩展侧的人工确认兜底);档位变化通告因此从不触发。现在改读sessionProjections.snapshot(session, ['permissions'])的 wire view,喂给 watcher 的值也来自同一份校验过的视图。离线契约校验新增 6 项,专门钉住「必须读发布视图、不得读stateOf、缺该能力时报告无档位」,这类静默降级以后不会再溜过。
*The bridge read the tier fromsessionProjections.stateOf(session, 'permissions'), which returns the projection unit's host state (preset/sandbox/approval/seeded) — not the tier view (options/currentValue), which the unit'sview()produces andviewSchemavalidates before any client sees it. The field names differ, soresolvePermissionalways gotundefined, concluded "this deployment has no tier capability", and the extension's fail-closed reading of an absent policy isask. The result was tiers silently doing nothing, with no error anywhere: picking full access still prompted for every write, picking read-only did not refuse page mutation at the bridge (only the extension's own confirmation stood in the way), and tier-change announcements never fired. The bridge now reads the wire view fromsessionProjections.snapshot(session, ['permissions']), and the value fed to the watcher comes from that same validated view. Six offline assertions pin "read the published view, neverstateOf, and report no capability when it is unavailable", so this class of silent downgrade cannot slip through aga...
bridge-browser v0.3.0
新功能 · New features
-
权限档位选择器 · Permission tier selector — 输入区左侧(紧接框选按钮)新增会话权限档位丸状下拉(模型选择器同时移到输入区右侧靠右对齐),标签与 dsh 界面一致(
仅可查看/工作区内修改/完全权限)。当前值与候选全部来自会话的permissions投影(currentValue+options),不硬编码档位清单:部署自定义的 preset 按其公布名称显示,桥接不认识的档位如实呈现为「自定义」且不可选。切换经permission.setRPC 执行,成功与否以投影回流为唯一判据,不靠写入调用的返回值,因此「写入成功但档位没变」不会被显示成切换成功。切到「完全权限」前必过一次勾选式风险确认(每次重新确认、不记住选择),文案同时说明「浏览器操作不再需要确认」与「该档位同时放开 dsh 侧的文件与命令权限」。同步有三条通道:会话历史投影为基线、桥接的session/permission事件(仅在档位实际变化时推送)为增量、切换时的乐观更新为即时反馈——冲突时以投影为准。「新会话」状态下也能先配档位:档位表从session.list里任一会话的投影读到(只读、不创建会话),选定后才惰性创建会话并提交切换。断开连接时控件禁用,切换失败按稳定错误码如实说明(unknownPreset/customNotSwitchable/capabilityUnavailable),不假装成功。
A permission-tier pill sits at the left of the composer (next to the region button), with the model selector moved to the right, labelled exactly as dsh labels its tiers (read-only/workspace-write/danger-full-access). Current value and candidates all come from the session'spermissionsprojection (currentValue+options) — no hardcoded tier list: a deployment's own preset shows under its published name, and a tier the bridge does not recognize is shown honestly as "custom" and cannot be picked. A switch goes through thepermission.setRPC and the projection flowing back is the only proof it worked — never the write call's return value, so "the write succeeded but the tier did not move" is never displayed as a successful switch. Choosing full access first requires a checkbox-style risk confirmation (re-confirmed every time, never remembered) that states both "browser actions will no longer be confirmed" and "this tier also opens dsh's file and command permissions". Three channels keep it in sync — the history projection as baseline, the bridge'ssession/permissionevent (emitted only on a real change) as the increment, and an optimistic update on switch as instant feedback — with the projection winning any conflict. A tier is configurable even on "new session": the deployment's tier table is read from any session insession.list(read-only, creating nothing), and the session is created lazily only once a tier is chosen and submitted. The control is disabled while disconnected, and a failed switch is reported honestly by its stable code (unknownPreset/customNotSwitchable/capabilityUnavailable) rather than as a success. -
页面分享控件:把既有的隐私偏好做成可见可撤销的开关 · Page-sharing control: the existing privacy preference, made visible and revocable — 系统配置弹框新增页面分享下拉(
自动/每次询问/关闭)。它与权限档位正交:档位决定智能体能做什么,这个偏好只决定页面内容能否离开页面送给模型——关闭时browser_snapshot/browser_get_text一律拒绝,且不产生审批请求。控件值随状态广播刷新,因此审批弹框里「总是允许读取」写入的偏好会在这里可见并撤销(此前面板没有任何入口能读到它)。改动在 change 事件上即时持久化生效,而非「保存并重连」,因为偏好下一次工具调用即生效。
System config gained a page sharing dropdown (auto/ask/off). It is orthogonal to the permission tier: the tier decides what the agent may do, this preference decides only whether page content may leave the page for the model — underoff,browser_snapshot/browser_get_textare refused and no approval request is produced. The control follows the status broadcast, so a preference written by the approval dialog's "always allow reads" is now visible and revocable here (previously the panel had no way to read it back). A change persists and takes effect on the change event rather than on "save and reconnect", because the preference applies from the next tool call. -
审批与快照策略改由共享工具注册表派生 · Approval and snapshot policy now derive from the shared tool registry — 扩展不再维护自己的「页面读取工具」「状态变更工具」「delta 附带工具」「导航候选工具」四张本地表:全部改为从
@dsh-browser/protocol的browser-tools.ts派生,注册表不认识的名字在扩展侧也判不出类别(桥接层已先以unknown-tool拒绝)。tool.call帧新增policy字段(桥接解出的本次授权),扩展原样应用:auto直接执行、不产生审批请求;ask走人工审批(无应答即拒绝);帧内没有该字段时按ask兜底,因此新扩展配旧桥接仍保持档位之前的「写操作一律确认」(升级顺序不会打开未确认的执行窗口)。页面 delta 的附带同样受隐私轴约束:关闭不附带,每次询问下只有「本就无需审批」或「刚刚获批」的调用才附带。
The extension no longer keeps its own four local tables of page-read tools, state-changing tools, page-delta tools and navigation-candidate tools: all derive frombrowser-tools.tsin@dsh-browser/protocol, and a name the registry does not know has no class on the extension side either (the bridge already refused it asunknown-tool). Thetool.callframe gained apolicyfield — the bridge's solved authorization for that call — which the extension applies verbatim:autoruns the action with no approval request,askraises a panel approval (no answer means refusal), and a frame without the field falls back toask, so a new extension against an older bridge keeps the pre-tier "confirm every write" behavior and no upgrade ordering opens an unconfirmed execution window. Page-delta attachment answers to the privacy axis too: nothing is attached underoff, and underaskonly a call that needed no approval — or was just approved — may attach it. -
共享词汇下沉、模块依赖单向化 · Shared vocabulary sunk into leaf modules; the dependency graph made acyclic —
background/types.ts收拢工具调用/应答/帧/内容预算类型,BridgeState、RegionElement、RegionRect与页面分享词汇下沉到common/,消除 panel→background 与 background→content 的类型依赖;content script 的动作分发改成按注册表名键控的查表,注册表声明了而实现缺失的名字以稳定错误失败,而不是执行错误的处理器。
background/types.tscollects the tool-call/answer/frame/content-budget types, whileBridgeState,RegionElement,RegionRectand the page-sharing vocabulary moved intocommon/, removing the panel→background and background→content type dependencies; the content script's action dispatch became a table keyed by registry name, so a name the registry declares but no implementation serves fails with a stable error instead of running the wrong handler.
修复 · Fixes
- 富文本编辑器里的输入不再落空 · Typing into a rich-text editor no longer lands nowhere — contenteditable 宿主此前只做
textContent直写 +input事件:这类编辑器的文档模型不认这次写入,下一次 reconcile 就把它丢掉,而「发送」按钮的可用状态恰恰来自那个模型,于是工具报告成功、页面一个字都没进(dsh 输入框是 Lexical,必现)。改为走浏览器自己的编辑命令execCommand('insertText')——先聚焦并把光标放进宿主、等一拍让编辑器认领选区,命令发出的beforeinput/input是编辑器真正监听的可信事件;随后回读内容确认落字,只有没落字时才退回直写,并在状态行如实说明该次未被编辑器接受。
A contenteditable host used to get atextContentwrite plus aninputevent. Such an editor keeps a document model of its own, never takes that write (its next reconcile drops it), and owns the submit button's enabled state — so the action reported success while not one character reached the page (guaranteed on the dsh composer, which is Lexical). Typing now goes through the browser's own editing commandexecCommand('insertText'): focus the host, place a caret, wait a beat for the editor to adopt the selection, and the command emits the trustedbeforeinput/inputpair these editors listen for. The content is read back to confirm the text landed; only when it did not does the direct DOM write remain, and the status line says the editor did not accept it.
Published from tag bridge-browser@0.3.0.
加载 · Load — unzip bridge-browser-0.3.0.zip, then open
chrome://extensions → enable Developer mode → Load unpacked → select the
unzipped folder. Open any http(s) page, click the extension icon, and wait for
「已连接 dsh」.
要求 · Requires — Chrome 116+, plus the bridge-dsh plugin on the dsh side.
完整变更 · Full changelog — packages/extension/CHANGELOG.md
bridge-dsh v0.2.0
新功能 · New features
-
斜杠命令与技能 RPC · Slash-command and skill RPCs — 新增
commands.list、commands.execute、skills.list三条 RPC,把扩展的斜杠词汇请求转发给网关。命令与技能是平行的两个命名空间:技能没有执行端点,调用方式是发一条普通session.prompt,由宿主 pre-step 边界的手势识别注入技能正文。这三条 RPC 的请求/响应契约在@dsh-browser/protocol中类型化,宿主侧字段改名从此是编译错误,而不是面板里静默消失的一列。
Three new RPCs —commands.list,commands.executeandskills.list— forward the panel's slash-vocabulary requests to the gateway. Commands and skills are parallel namespaces: a skill has no execute endpoint and is invoked by sending an ordinarysession.prompt, whose/namegesture the host's pre-step boundary answers by injecting the skill body. Their contracts are typed in@dsh-browser/protocol, so a host-side field rename is a compile error instead of a silently missing column. -
会话归入工作区分组 · Session workspace grouping — 新配置项
sessionWorkspace:开启后,扩展创建的会话会先把该目录幂等注册为 dsh 工作区,再注入解析出的workspaceId,使它们按目录分组显示在 dsh GUI 侧边栏,而不是堆在「未分组」桶里。分组只由工作区的成员账(sessionIds)决定,比较cwd不会产生分组;注册失败绝不阻塞会话创建,失败也不会被永久固化为「不再尝试」。
NewsessionWorkspaceconfig: extension-created sessions first register that directory as a dsh Workspace (idempotently) and then carry the resolvedworkspaceId, so they group under the directory in the dsh sidebar instead of piling up in "Ungrouped". Grouping is decided solely by the Workspace's membership account (sessionIds) — comparingcwdproduces none. A failed registration never blocks session creation, and a failure is never cached as "stop trying". -
共享地址规则 · Shared endpoint rules —
@dsh-browser/protocol新增endpoint.ts:把用户输入的地址(10.0.0.7:3080、localhost:3080、wss://dsh.example.com、完整ws://host:port/ext/bridge)规范化为桥的 WebSocket URL,两端共用同一份规则。
@dsh-browser/protocolgainedendpoint.ts: the rules that turn a user-typed address (10.0.0.7:3080,localhost:3080,wss://dsh.example.com, a fullws://host:port/ext/bridge) into a bridge WebSocket URL — one implementation shared by both halves.
修复 · Fixes
-
保序 RPC 的有界等待 · Bounded wait for session-ordered RPCs — 按会话串行化会一直占着该会话的队列槽位,而宿主命令执行端点的应答要等 handler 结束才产生,于是
/compact(压缩全量对话)这类长命令会把它之后所有session.prompt/session.cancel/commands.execute无限期挡住 —— 用户看到的是「消息发出去没反应」。保序调用现在另有一道 120s 有界等待:超时后中止该调用自身的 signal、以rpc.result失败收尾(错误码timeout)并释放队列槽位。deadline 在调用真正开始执行时才 arm,排队等待的时间不计入预算。
Per-session serialization held a session's queue slot until the call settled, and the host's command endpoint answers only after its handler finishes — so a long command such as/compactblocked every latersession.prompt/session.cancel/commands.executeon that session indefinitely. Ordered calls now run under a 120s bound: on expiry the bridge aborts that call's own signal, ends it as a failedrpc.result(codetimeout) and releases the queue slot. The deadline is armed when the call actually starts, so time spent queued is not charged to it. -
超时文案不声称已取消 · The timeout report never claims cancellation — 宿主无法被强制停止一个已在运行的 handler(其
withAbort只包装 Promise),所以失败文案只说「未在时限内应答、可能仍在执行、结果以事件流为准」。实测:/compact会真的停止并自行补一条command/done;/goal、/permission、/feedback、/export不观察 signal,会跑完。
A host handler already running cannot be forced to stop (itswithAbortonly wraps a promise), so the failure text says only that the call did not answer within the limit, may still be running, and that the event stream owns the outcome. Measured:/compactreally does stop and appends its owncommand/done;/goal,/permission,/feedbackand/exportdo not observe the signal and run to completion. -
工作区注册不再借用调用方的连接 · Registration no longer borrows the caller's connection — 注册曾缓存在首个调用方 signal 上构造的 promise,连接被替换时该 signal 失效,后续注册就可能沿用一个已死的生命周期。注册现在持有自己的
AbortController;已中止的调用方直接返回,不再发起一次注定被丢弃的注册。
Registration cached a promise built from the first caller's signal, so a connection replacement could leave later registrations on a dead lifecycle. Registration now holds its ownAbortController, and an already-aborted caller returns without starting a registration doomed to be discarded. -
sessionId收紧为 trim 后非空 ·sessionIdtightened to trimmed non-empty — 纯空白 ID 不再被当作查找键:三条命令路径与线协议解析器对tool.call的既有处理由此一致,一律bad-request。
A whitespace-only id is no longer used as a lookup key — the three command paths now match what the wire parser already does fortool.call:bad-request. -
发布说明固定版本而非
@latest· Release notes pin the version instead of@latest— 安装说明不再给@latest:pnpm 11 的minimumReleaseAge默认 1440 分钟,新发布的版本会被挡下,而@latest会静默装成上一个版本(不报错)。说明改为固定版本 +--config.minimumReleaseAge=0。
The install instructions no longer advertise@latest: pnpm 11'sminimumReleaseAgedefaults to 1440 minutes, so a fresh release is held back and@latestsilently installs the previous version rather than failing. They now pin the version and pass--config.minimumReleaseAge=0.
工具链 · Tooling
pnpm typecheck现在覆盖packages/protocol与packages/bridge-dsh(此前只覆盖扩展)。
pnpm typechecknow coverspackages/protocolandpackages/bridge-dsh(previously only the extension).
Published from tag bridge-dsh@0.2.0.
安装 · Install — pin the version. A bare @latest is held back by pnpm's
minimumReleaseAge (24h default) and silently resolves to the previous
release instead of failing:
dsh plugin --profile web add -w "bridge-dsh@0.2.0" --config.minimumReleaseAge=0要求 · Requires — dsh ≥ 0.1.2-rc.1 (the 0.1.x Typert Gateway + Connection architecture).
完整变更 · Full changelog — packages/bridge-dsh/CHANGELOG.md
bridge-browser v0.2.0
新功能 · New features
-
斜杠命令与技能 · Slash commands & skills — 输入
/在输入区上方弹出可过滤、可键盘操作的菜单,列出所绑定会话解析到的两组斜杠词汇:宿主命令(commands.list:名称 + 宿主描述 + 宿主公布的参数提示)与用户可调用技能(skills.list:名称 + 描述,仅用户可调用的标注「仅用户」)。同名时命令优先(与宿主裁定一致)。选择只把/<name>写回输入框,既不执行也不发送;/开头但两组都不解析的文本(如/etc/hosts)按普通消息发送。两组词汇的调用方式不同:命令走
commands.execute(整行透传,含参数),不建 turn、不产生用户消息、不开启「正在分析」指示器;技能没有执行端点,作为普通消息发送,由宿主注入技能正文。命令的一生只由持久事件command/run/command/done驱动渲染,因此实时与历史重放共用同一条渲染路径、天然一致。Typing
/opens a filtered, keyboard-navigable menu over the bound session's two slash vocabularies: host commands (commands.list: name + host description + host-published argument hint) and user-invocable skills (skills.list: name + description, user-only skills marked). A name published by both resolves to the command, matching the host's own adjudication. Picking only writes/<name>back into the composer — it neither executes nor sends; a/-prefixed draft naming nothing in either namespace (e.g./etc/hosts) is sent as an ordinary message.The two vocabularies are invoked differently: a command goes through
commands.execute(whole line, arguments included) and opens no turn, no user message and no "working" indicator; a skill has no execute endpoint and is sent as an ordinary message for the host to answer by injecting the skill body. A command's life is rendered only from the durablecommand/run/command/doneevents, so the live view and history replay share one path and agree by construction. -
会话选择 · Session picker — 状态栏「会话」下拉,首项恒为「新会话」。候选来自只读的
session.list(排除空会话与子代理会话、按最近活动倒序、最多 50 项并说明截断),每项以[工作目录名]前缀 + 标题 + 运行中标记 + 相对时间呈现。选中历史会话即绑定并重放session.history(先清空再按序重放);打开与重连都不创建会话,因此在 dsh 里不再留下孤儿会话。A "session" dropdown in the status bar, "new session" always first. Candidates come from the read-only
session.list(blank and sub-agent sessions excluded, most-recent-first, capped at 50 with the cut-off marked), each showing a[working-directory]prefix, title, running marker and relative time. Picking a past session binds it and replayssession.history; opening or reconnecting never creates a session, so the panel no longer leaves orphan sessions behind in dsh. -
远端 dsh 配置 · Remote dsh configuration — 状态栏齿轮按钮打开「系统配置」,填
dsh host+token,保存前可点「测试连接」做一次独立握手(不顶替正在工作的连接),并分别报告失败阶段:地址畸形 / 不可达 / 可达但 token 被拒(4002)/ 可达但握手超时。地址接受四种写法;host非空时不再回落本机自动发现——配错的远端地址必须表现为连不上,而不是悄悄连上本机。A gear button opens System config for
dsh host+token, with a Test connection that runs one isolated handshake (never evicting the working connection) and reports the failure stage separately: malformed address / unreachable / reachable but token rejected (4002) / reachable but handshake timed out. Four address forms are accepted; a non-emptyhostnever falls back to local discovery — a wrong remote address must read as unreachable, not quietly connect to this machine. -
新品牌图形 · New brand mark — 图标资产整体替换为橙色圆形人物 + 单片眼镜造型(透明背景、墨色描边),5 个位图尺寸由同一母版派生、构图一致;侧边栏状态图标改为以 1:1 尺寸引用
icon16.png,不再由浏览器二次降采样。
The icon set was replaced with an orange round-faced character wearing a monocle (transparent background, ink outline). All five bitmap sizes derive from one master with identical composition, and the sidebar status icon now referencesicon16.pngat 1:1 instead of being downsampled by the browser.
修复 · Fixes
-
输入法组字期间的 Enter 不再被菜单抢走 · An Enter pressed during IME composition is never consumed — 该按键属于候选词上屏,不是选择条目。
isComposing与keyCode === 229都判,因为提交候选的那个 Enter 可能在compositionend之后才到达。
That key accepts the candidate rather than picking a row. BothisComposingandkeyCode === 229are checked, because the committing Enter can arrive aftercompositionend. -
切会话不再被上一会话的在途请求吞掉 · A session switch is no longer swallowed by the previous session's in-flight request — 目录请求的合流守卫改为按会话键判定,并加连接世代使迟到响应被丢弃。此前在新会话下唤出菜单会走到「命令目录不可用」,而事实相反:目录可用,只是请求被守卫吞了。
The catalog request guard is now keyed by session, with a connection generation so late responses are dropped. Previously, opening the menu after a switch reported "command list unavailable" when the opposite was true: the catalog was fine, the request had simply been swallowed. -
断开连接即清空目录缓存 · The catalog is cleared when the connection drops — 缓存里的条目指向一个面板已经够不着的宿主,重连前唤出菜单会如实显示「命令目录不可用」,而不是旧宿主的命令。
Cached entries name a host the panel can no longer reach; before reconnecting, the menu now reports "command list unavailable" instead of showing the previous host's commands. -
描述缺失不再丢掉整条命令 · A missing description no longer costs the whole entry — 目录条目改为只以名称为必需字段。此前宿主未公布描述会导致该条目被丢弃,于是手打该命令会落回普通消息送给模型,把命令行当提示词消耗。
A catalog entry now requires only its name. Previously an unpublished description dropped the entry entirely, so typing that command fell back to an ordinary message and spent the command line as a prompt. -
命令超时如实回报 · Command timeouts are reported honestly —
commands.execute可能超出客户端等待预算(如/compact)。面板只提示「未在时限内应答、可能仍在执行、结果以事件流为准」,不声称失败或已取消——命令的真实结论只由command/run/command/done给出。
Acommands.executecan outlive the client's wait budget (e.g./compact). The panel says only that it did not answer within the limit, may still be running, and that the event stream owns the outcome — never that it failed or was cancelled. -
选择条目不再无条件重刷目录 · Picking an entry no longer re-fetches the catalog — 选择条目不可能改变会话,此前一次点击会发两个 RPC(且
commands.list会 resume 会话)。刷新点收敛为「会话绑定 / 连接建立 / 首次唤出 / prompt 被受理」。
Picking cannot change the session, yet a click used to fire two RPCs (andcommands.listresumes a session). Refresh points are now: session bind, connect, first menu open, and prompt accepted. -
菜单不再沉默 · The menu is never silent — 「正在创建会话…」「正在加载命令…」「命令目录不可用,重连后重试」「没有匹配的命令」四种情形各有一条不可选中的说明行。技能目录失败只降级为「本次没有技能」,不牵连命令。
Four explanatory, non-selectable rows cover "starting a session…", "loading commands…", "command list unavailable — reconnect to retry" and "no matching command". A failed skill catalog degrades to "no skills this time" without taking the commands down.
Published from tag bridge-browser@0.2.0.
加载 · Load — unzip bridge-browser-0.2.0.zip, then open
chrome://extensions → enable Developer mode → Load unpacked → select the
unzipped folder. Open any http(s) page, click the extension icon, and wait for
「已连接 dsh」.
要求 · Requires — Chrome 116+, plus the bridge-dsh plugin on the dsh side.
完整变更 · Full changelog — packages/extension/CHANGELOG.md
bridge-dsh v0.1.0
新功能 · New features
-
model.catalogRPC — 只读、进程内直读 dsh 的llm+agentDefaultModel,返回部署默认模型与按 provider 分组的模型目录(含inputModalities多模态能力);单 provider 故障只记入failures,服务缺失时返回llm-unavailable,不影响连接与其它 RPC。
Read-only, in-process projection of dsh'sllm+agentDefaultModel: deployment default + provider-grouped catalog withinputModalities(multimodal capability), per-provider fault isolation intofailures, cleanllm-unavailabledegradation. -
共享 prompt 契约
protocol/prompt.ts— 分段标签常量 + 区域截图 prompt 组装函数,两端唯一真相源,替代散落的字符串字面量。
Shared prompt contractprotocol/prompt.ts: section-label constants + region-prompt builders as the single source of truth for both halves. -
协议类型 · Protocol — 新增
PromptImagePart(prompt 图片载荷)与区域捕获上限常量MAX_SCREENSHOT_BYTES/MAX_REGION_ELEMENTS。
AddedPromptImagePartand region-capture limitsMAX_SCREENSHOT_BYTES/MAX_REGION_ELEMENTS.
修复 · Fixes
- 无。本次为纯增量:帧结构不变,审批 / token / 隐私边界均未削弱。
None — additive only; frame shapes and the approval / token / privacy boundaries are untouched.
bridge-browser v0.1.0
新功能 · New features
-
框选截图 · Region capture — 面板拖拽框选页面区域 → 裁剪截图 + 选区内 DOM 元素清单打包进 prompt 发给视觉模型;非视觉模型自动降级为纯元素清单。
Drag-select a page region → cropped screenshot + intersecting DOM element list → vision-capable model; non-vision models degrade to the element list. -
模型选择 · Model selection — 面板下拉列出模型目录并标注多模态能力(视觉/文本/未知),选定即经
session.selectModel生效并同步会话实际选中。
Dropdown over the model catalog with a capability badge (vision/text/unknown); choosing callssession.selectModeland stays in sync with the session. -
Markdown 渲染 · Markdown rendering — assistant 回复用 marked + DOMPurify 渲染为富文本(白名单消毒,防提示注入)。
Assistant replies render as rich text via marked + DOMPurify (allow-listed sanitization).
修复 · Fixes
-
选区清单去重 · Region list dedupe — 收紧元素入选判据(去掉「有 class 即描述」)、容器用直接文本而非整页
textContent、排除自注入 overlay/box、补齐文本语义标签。
Tightened inclusion criteria, direct-text summaries for containers, exclude self-injected overlay/box nodes, more text-semantic tags. -
预判降级 · Predictive degrade — 已知纯文本模型直接发元素清单,省掉「先发图→失败→重发」往返。
Known text-only models skip the guaranteed image round-trip. -
重构 + 类型安全 · Refactor + type safety — 抽出可复用
common/(tools + ui),面板拆成 8 个单一职责模块;新增tsc类型检查并修掉历史类型债。
Reusablecommon/(tools + ui), panel split into 8 single-responsibility modules, andtsctype-checking added with pre-existing type debt fixed. -
CI 修复 · CI fix — marked/dompurify 声明为依赖并在构建时优先从 node_modules 解析(修复硬编码本地路径导致的 CI 构建失败)。
marked/dompurify are declared deps resolved from node_modules (fixes the hardcoded local-path build failure).
bridge-dsh v0.0.3
bridge-dsh v0.0.3
dsh 侧桥插件(Cordis)—— 让 dsh 读取、操作你正在使用的真实浏览器标签页。
The dsh-side bridge plugin (Cordis) for reading and operating the user's real browser tabs.
功能 / Features
- WebSocket 桥:
/ext/bridge挂载 token 鉴权的 WebSocket 升级路由,/ext/bridge-config零配置发现。
Token-authenticated WebSocket bridge at/ext/bridge+ zero-config discovery at/ext/bridge-config. - 12 个
browser_*工具:browser_snapshot、browser_click、browser_type、browser_press、browser_scroll、browser_navigate、browser_open_tab、browser_back、browser_forward、browser_reload、browser_get_text、browser_wait。
12browser_*tools: snapshot / click / type / press / scroll / navigate / open_tab / back / forward / reload / get_text / wait. - 纯文本管线:页面渲染为结构化文本快照,模型按编号寻址元素,全程不截图。
Text-only pipeline — pages become structured text snapshots addressed by number, no screenshots. - 安全模型:读默认放行,写操作 fail-closed 需侧边栏审批;bearer token 首启自动生成(0600 持久化)、恒定时间校验。
Security: reads auto-allowed, writes fail-closed behind side-panel approval; bearer token auto-generated on first boot (0600), constant-time compare. - Opt-in 设计:只有出现在 profile bundle 里才生效,不改 dsh 核心。
Opt-in by design — no dsh core changes.
要求 / Requirements
- dsh ≥
0.1.2-rc.1(0.1.x 的 Typert Gateway + Connection 架构;已在0.1.3-alpha.1验证)
dsh ≥0.1.2-rc.1(0.1.x Typert Gateway + Connection architecture; verified on0.1.3-alpha.1)
安装 / Install
dsh plugin --profile web add -w "bridge-dsh@0.0.3"bridge-browser v0.0.2
bridge-browser v0.0.2
浏览器侧 Chrome MV3 扩展 —— 侧边栏 + service worker + content script,纯文本读取/操作当前标签页。
The browser-side Chrome MV3 extension — side panel + service worker + content script for text-only read/operate of the controlled tab.
功能 / Features
- 结构化快照 + 稳定编号:页面渲染为标题/URL/正文/编号控件/表单,模型按稳定编号点击、输入、滚动、导航。
Structured snapshots with stable element numbering — click / type / scroll / navigate by number. - 侧边栏对话:连接状态、日志、审批框、问答框;dsh 的
ask_user_question显示在面板。
Side panel: chat, connection status, logs, approval & Q&A; dshask_user_questionrenders here. - 自动发现 + 断线重连:探测端口 →
fetch /ext/bridge-config→ WebSocket;指数退避重连(500ms→10s 封顶)。
Auto-discovery + reconnect with exponential backoff (500ms → 10s cap). - 页面感知:追踪活动标签页,把 URL/标题注入每条消息作为上下文。
Page awareness — tracks the active tab and injects its URL/title into each prompt. - 隐私:密码/卡号按类型与字段名判定掩码(••••),永不回传;文本不截图。
Privacy — passwords/card numbers masked, never sent; text-only (no screenshots). - 图标:新增 16/32/48/128 图标。
Icons — added 16/32/48/128 icons.
要求 / Requirements
- Chrome 116+
- 配合 dsh 侧的
bridge-dsh插件 / Works with thebridge-dshplugin on the dsh side.
安装 / Install
解压 bridge-browser-0.0.2.zip → chrome://extensions → 开启「开发者模式」→「加载已解压的扩展程序」。
Unzip bridge-browser-0.0.2.zip → chrome://extensions → Developer mode → Load unpacked.