DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

wuhehen176 /

wuhehen176/ST-bridge

Verified

DSH persistent Agent session bridge for Tavern plugins

★ 6 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@b63fb305

ST-bridge

ST-bridge 是供柏宝绘、玉子手机、数据库等酒馆插件调用的本机持久会话桥接: http://127.0.0.1:43121/v1。

仓库:https://github.com/wuhehen176/ST-bridge

两个可安装部分分别发布:

部分 安装源
DSH Host 插件 https://github.com/wuhehen176/ST-bridge
酒馆 / TauriTavern 扩展 https://github.com/wuhehen176/ST-bridge-tavern

酒馆扩展使用独立仓库,因为酒馆要求仓库根目录直接包含 manifest.json。

本机跨插件的来源预览与手动规则档位见 上下文适配第一版。 默认所有材料保留,仅处理显式配置的本机桥接请求。

玉子手机、数据库等插件建议使用各自的 /clients/插件ID/v1 地址,避免没有来源 标识的请求共用一个 Agent。电脑设置页的「插件接入」可登记、停用和复制地址, 并为每个插件单独选择模型、Agent 预设、工作区和 max_tokens;留空跟随 DSH 当前默认。配置步骤见 插件专属地址与聊天隔离。

更新本项目代码后,需要完全退出并重启 DSH Desktop才能加载新版本。 DSH Host 插件不会热替换已经加载的代码。vendor 和 node_modules 可能是 两份独立副本;仅复制 vendor 或仅比较版本号,不能证明运行进程已更新。 请用下方部署检查脚本核对解析入口、磁盘副本和进程加载指纹。

这不是一个直接转发模型 API 的代理,而是一个 DSH Host 插件。每个酒馆 chat_id 绑定一个真实的 DSH Agent 会话;会话通过 ctx.agents 创建,因此会 出现在 DSH 界面中,可以手工打开、继续交流、查看历史。桥接只负责:

  • 支持 dsh_session v1:固定组/动态组、generate/sync_rules/reset;
  • 按 client_id + chat_id + task_id 隔离插件、聊天和子任务会话;
  • 把首次完整上下文送进新会话;
  • 后续只追加新增正文和本轮要求;
  • 用 request_id 做幂等,避免网络重试重复追加;
  • 同一 chat_id 的生成请求串行执行;
  • 重写旧楼层、swipe、回退剧情时重建分支会话;
  • 等 Agent 回合结束,只把最终正文放进 choices[0].message.content。

0.12.0 起支持客户端原生函数工具的往返:Agent 返回 tool_calls,由客户端执行, 回传 role: "tool" 后继续同一个 Agent 回合。第一版支持范围和验收方式见 外部工具接入。

桥接不解析、不包裹、不增加代码围栏,所以柏宝绘的 images/changes JSON 会原样返回。

目录

文件 作用
index.js DSH 插件入口,读取配置、启动 43121 端口
lib/engine/ 会话创建/恢复、上下文分类、发送、等待、分支、幂等(index.js 类壳,*-methods.js 分组)
lib/context.js 消息规范化、前缀 diff、首次/追加/重建提示词
lib/session-protocol.js dsh_session 校验、固定规则格式化、稳定 session ID、采样参数
lib/turn.js 从 DSH 事件日志提取最终回答与 usage
lib/store.js chat_id -> session_id 映射和 request_id 记录的持久化
lib/server.js OpenAI 兼容 HTTP/SSE 层
lib/external-tools.js 客户端工具注册、异步等待回填、会话隔离与幂等
lib/panel.js DSH 设置页 Host RPC、设置持久化、scope/规则/危险操作
client/ DSH 设置页源码(入口、面板、插件页、helpers、theme)
client.js 由 npm run build:client 生成的浏览器 bundle
install.mjs 安装到当前 DSH profile
deploy.mjs 更新前检查、备份同步与重启后的运行指纹检查
lib/build-info.js 捕获本次进程加载的版本、入口与 SHA-256 指纹
cordis.patch.yml npm/bundle 安装时使用的插件包 patch
cordis.patch.snippet.yml 手工安装片段
assets/preset-persona.txt 桥接专用最小系统提示词

安装

要求:DSH Desktop 已正常运行,Node.js 22.19+。

cd D:\AIGC\MOSS\ST-bridge
node install.mjs --cwd D:\AIGC\MOSS --port 43121

脚本会:

  1. 在 <profile>/node_modules/ST-bridge 创建指向本目录的链接;
  2. 备份并修改 <profile>/cordis.patch.yml,追加一个受管配置块;
  3. 注册 Agent 预设 st-bridge,提供文本系统提示词与本地 Skill 注册表, 由桥接只注入管理员绑定的 Skill。工具跟随当前选择的 Agent 预设: st-bridge 预设默认不加载 DSH 工具;选择 standard 等其他预设时, 桥接不再清空该预设的工具,执行权限仍由 DSH 工具策略决定。 酒馆人格与运行时上下文仍由桥接管理。

安装后必须 完全退出 DSH Desktop(托盘退出,不是只关窗口),再重新启动。 然后验证:

curl http://127.0.0.1:43121/health
curl http://127.0.0.1:43121/v1/models

预期:

{"object":"list","data":[{"id":"dsh-default","object":"model"}]}

卸载:

node install.mjs --uninstall

手工安装可参考 cordis.patch.snippet.yml。配置块也可以直接写进 ~/.dsh/profiles/desktop/cordis.patch.yml 末尾。

更新与部署检查

在准备发布的源码目录执行,Node.js 22.19+,无需额外依赖:

node deploy.mjs --check

默认是只读检查。脚本通过 Node 的模块解析确定 profile 实际加载的 ST-bridge 入口,并检查现存 vendor 副本。发现待同步文件时退出码为 1; 磁盘一致时为 0。这里检查的是磁盘,尚不能证明运行进程已更新。

确认当前没有生成任务,从托盘完全退出 DSH Desktop,再执行:

node deploy.mjs --apply

脚本会检查 DSH 进程和本机桥接端口均已停止,再对发布包里的 JS 做语法检查。 它备份待修改文件到 <profile>/.st-bridge-backups/<时间与随机编号>/, 同步实际安装副本和 vendor,移除旧发布清单中的过时代码,并逐文件比对内容。 复制或校验失败时回滚本次修改,备份保留。若检查期间目标文件被其他程序 改动,脚本停止,要求重新检查。它不会改 profile 配置、设置、会话数据, 也不会安装依赖或自动启动、退出 DSH。源码链接安装会跳过自我复制。

手动启动 DSH 后执行:

node deploy.mjs --verify-running

只有磁盘副本一致,且 /health 报告的加载时指纹与入口都与本次源码及 所选 profile 对应,才返回成功。版本号相同、未重启的旧进程、旧版没有指纹, 都不能通过。启动日志同时记录 [build] version=... sha256=... entry=...。 指纹在模块加载时捕获,之后覆盖磁盘文件不会改变该进程报告的值。

默认 profile 是 ~/.dsh/profiles/desktop,默认从 ~/.dsh/st-bridge/settings.json 读取桥接端口与令牌(令牌不会打印)。可指定:

node deploy.mjs --check --profile C:\path\to\profile
node deploy.mjs --verify-running --port 43121 --settings C:\path\to\settings.json

需要同时指定 profile 与 settings 时,将两个选项一起传入。若认证仅写在 Cordis 配置而未保存到本地设置,先通过 DSH 桥接设置页保存,或指定含当前 令牌的本地 settings 文件。运行检查只连接 127.0.0.1,不跟随重定向。 自动部署的进程检查目前支持 Windows;--check 可跨平台使用。

DSH 设置面板

重启后,DSH「设置」里会出现「ST-bridge」页,包含:

  • 服务:启停、host/port、Bearer token、CORS、allowLegacyPlain、 请求超时、请求体上限。host/port 修改后点「保存并重启」。
  • 插件接入:每个插件条目可单独选择模型(按 Provider 分组)、Agent 预设、 工作区 cwd 和默认 max_tokens;留空跟随 DSH 当前默认。修改只影响新创建/ 重新恢复的会话,已有活动会话需重置绑定或新建会话。reasoning_effort 只在模型支持该档位时传递;不支持会自动忽略,不会让整个回合报错。 勾选「持续会话」后,该插件的所有聊天共用一个 DSH 窗口,固定规则以最后一次 sync_rules 为准,切换聊天 ID 不再新建会话;快照仍按聊天分别记录,前两轮 generate 发送完整上下文,第三轮起才做增量。切换开关会清掉该插件旧窗口; 关闭后各聊天从新会话开始,不会恢复旧窗口。
  • 客户端:默认按 client_id + chat_id + task_id 列出绑定,支持筛选、允许 client_id 列表、查看当前固定规则、清除规则、重置绑定。开启持续会话的插件只按 client_id 共用一个窗口,重置任一聊天会清掉该插件共享窗口并影响所有聊天。 重置不删除 DSH 会话历史。
  • 高级:折叠区包含运行诊断、危险操作和原始设置 JSON。运行诊断显示运行状态、 scope/会话数、capabilities、日志路径与最近日志;危险操作可停止/启动/重启桥接、 清空全部绑定,清空需要输入 RESET,停止前会二次确认;原始设置可直接编辑 settings.json 对应的 JSON,不展示真实 token。

设置保存到 ~/.dsh/st-bridge/settings.json(权限 0600)。界面里的 token 输入框留空表示保持现有 token;点「清除」才会移除。

从 baobaohui-bridge 升级

旧名 baobaohui-bridge 已改为 ST-bridge:

  • profile 依赖/bundle 改为 ST-bridge;
  • 状态目录改为 ~/.dsh/st-bridge,旧目录保留为备份;
  • 旧的 session-bbh-* 会话仍可通过本地绑定恢复;
  • 新会话使用 session-st-* 前缀;
  • API 地址、client_id、chat_id、task_id 协议不变。

分发与其他电脑

这是一个 DSH Host + Client 插件,不是脱离 DSH 的独立服务。换电脑时整目录复制, 目标机器必须有兼容的 DSH Desktop/Host、Node.js 22.19+、已配置的模型账号。

安装有两种方式,二选一:

  1. 本地链接安装(当前 install.mjs):创建 <profile>/node_modules/ST-bridge 链接,并把 preset/plugin 行写进 <profile>/cordis.patch.yml。
  2. 包/bundle 安装:把本包加入 profile 的 dsh.profile.bundles,或通过 dsh plugin --profile <name> add <本包> 安装;package.json 已声明 dsh.bundle.patch,cordis.patch.yml 会插入同样的 preset/plugin 行。

两种方式不要同时做,否则会出现重复的 st-bridge 行。 install.mjs 检测到 profile 已把本包列为 dependency/bundle 时会直接停止,避免重复插入。

换机器时:

cd <本目录>
node install.mjs --cwd <目标工作区> --port 43121 --profile desktop
  • Windows 用 junction,macOS/Linux 用符号链接。
  • 目标机器需重新配置模型通道/API Key;本仓库不携带任何密钥或聊天内容。
  • state.json 是本机 chat_id -> session_id 映射,不建议跨机直接复用;新机器首次 请求会返回 410 并重新初始化。
  • 默认只监听 127.0.0.1。其他电脑要调用,需要在面板开启 allowRemote、设置 token、放行防火墙,并把客户端 Base URL 改成目标机地址。
  • 当前按 DSH 0.2.1-rc.2 的 service/slot API 实现;DSH 大版本升级后若 settings.section、connection.rpc 等改名,需要适配。

柏宝绘 dsh_session v1(推荐)

请求仍是普通 OpenAI 非流式 JSON,额外在顶层带一个 dsh_session 对象。 DSH 用 fixed_message_count / fixed_message_types 把 messages 切成 「固定组」和「动态组」,固定组按用途保存为该会话的最新规则。

{
  "model": "dsh-default",
  "stream": false,
  "temperature": 0.6,
  "max_tokens": 8192,
  "messages": [
    {"role":"system","content":"固定前缀"},
    {"role":"system","content":"生图提示词规范及版式清单"},
    {"role":"system","content":"宿主输出协议"},
    {"role":"system","content":"输出前检查指导"},
    {"role":"user","content":"本次角色库、上下文、目标正文与任务"}
  ],
  "dsh_session": {
    "protocol_version": 1,
    "client_id": "baibai-image",
    "chat_id": "opaque-plugin-chat-identity",
    "task_id": "default",
    "request_id": "9d55d2dfce15d5210fd95890c973c039a",
    "action": "generate",
    "fixed_message_count": 4,
    "fixed_message_types": ["prefix","spec","contract","check"],
    "target": {"floor":12,"swipe_id":0,"revision":"opaque-target-revision"}
  }
}

字段

字段 含义
protocol_version 固定整数 1
client_id 可选插件身份,例如 baibai-image、yuzi-phone、shujuku;缺省按旧柏宝绘处理
chat_id 插件生成的不透明聊天标识;不能只用角色显示名
task_id 可选子任务标识,缺省 default;不是每轮随机 ID
session_id 首次没有;后续由桥接返回并携带
request_id 单次操作 ID;网络重试必须复用,桥接做幂等
action generate / sync_rules / reset
skill_id 可选;只能指定当前入口管理员绑定的 Skill 名称
skill_enabled 可选布尔值;false 关闭本次 Skill,缺省跟随管理员绑定;新请求更新版本,重试冻结版本
fixed_message_count messages 开头多少条属于固定组
fixed_message_types 与固定组逐条对应:prefix、spec、contract、check
target 仅生成时携带的元数据;桥接只记录,不放进正文

如果客户端只能填静态 YAML body、无法生成 request_id,可以省略它;桥接会用 chat_id + action + messages 的 SHA-256 自动生成 auto-...。相同 body 重试仍然 幂等,适合数据库这类固定预设:

dsh_session:
  protocol_version: 1
  client_id: shujuku
  chat_id: shujuku-db
  task_id: database
  action: generate
  fixed_message_count: 0
  fixed_message_types: []

注意:chat_id 写死意味着所有数据库调用共用一个持久会话。需要按酒馆聊天隔离时, 必须让客户端把 chat_id 换成动态值,而不是写死。

同一 chat_id 的后续请求即使不传 session_id,也会复用已绑定的会话;只有固定组 内容变化,或旧会话返回 410 时,才会重建。这样静态 YAML 预设不会每次调用都新开 一个 DSH 聊天。

client_id / task_id 也可放在顶层 body.client_id / body.task_id 或 x-client-id / x-task-id 头;chat_id / request_id 仍支持顶层与请求头 兼容写法。

fixed_message_count 可以为 0;未配置某类规则时,固定组可以少于四条, fixed_message_types 必须与固定组一一对应。动态组可以包含多条 system (角色卡、世界书)和多条 user(角色库、记忆、上下文、目标正文)。

多插件与子任务隔离

会话索引是 (client_id, chat_id, task_id) 的组合(服务端用 SHA-256 规范化, 不把组合键截断后当唯一身份)。不同插件或不同 task_id 即使 chat_id 相同,也会得到不同 DSH 会话;generate / sync_rules / reset 都只作用于 当前范围。

  • client_id 缺省:按旧柏宝绘的 chat_id 绑定查找,旧 session_id 继续有效。
  • 显式 client_id: "baibai-image" + task_id: "default":同样复用旧绑定。
  • 其它 client_id 或非默认 task_id:使用独立的新范围,不会静默迁移旧会话。
  • 界面标题使用可读插件名(柏宝绘 / 玉子手机 / 数据库)与聊天名;非默认 task_id 也会出现在标题里。

已知客户端标识:baibai-image、yuzi-phone、shujuku;其它稳定标识也接受。 chat_id / task_id 只作为不透明数据,不作为磁盘路径。

客户端接入要点:

  • 柏宝绘:client_id: "baibai-image",继续使用固定组; 旧请求不带 client_id 时仍命中旧绑定。
  • 玉子手机:client_id: "yuzi-phone",建议用 task_id 区分 QQ 私聊/ 群聊/主动周期;手动与主动请求都要带同一个聊天身份。
  • 数据库:client_id: "shujuku",数据库自身身份独立。
  • 玉子手机借用数据库 callAI:出口仍须保持 yuzi-phone,不能到 transport 再改身份。
  • 新插件:使用稳定 client_id;无法判断固定规则用途时用 fixed_message_count: 0,每轮发送完整任务材料。
  • 浏览器侧需通过酒馆 custom source 的 custom_include_body 透传 dsh_session;普通 openai source 可能在代理层丢掉扩展字段。

三种 action

generate(首次)

没有 session_id:创建独立、可在 DSH 中查看的 Agent 会话,保存固定组, 并用本次动态组执行一次生成。返回 dsh_session.session_id。

generate(后续)

携带 session_id,fixed_message_count: 0、fixed_message_types: [], messages 只有动态组。桥接在同一会话内继续执行,固定规则由该会话的 系统段自动注入,不需要插件重复传输。

固定组不会作为可见 user 消息重复塞进会话;DSH 把它保存为当前会话的 「最新确认版固定规则」系统段,每次生成自动带上。这样既减少重复传输, 也保证后续轮次继续生效。

动态组开头的 system 消息(角色卡、世界书等)同样作为「本轮动态系统资料」 注入 system 段,不挤进 user 正文;动态组其余消息保持原顺序和 role 标记。

系统段快照(0.16.2)

后续轮次的系统段由 systemSnapshot.mode 控制,默认 native。旧版会把系统段 替换成空的 system/message,并假设 DSH 会重新渲染;当前 DSH 不会,模型从第二轮 起会丢失世界书、固定规则和 Skill。native 不再替换正常节点,只在检测到旧版 遗留的空系统节点时补写本轮完整系统段;filled 每轮替换为完整渲染文本; empty 仅用于复现问题,正常不要使用。可在设置页「系统段快照」中切换。

0.16.2 起,系统段始终取本轮动态组的全部开头 system 消息,不再只取快照 diff 后的剩余消息。否则未变化的世界书会被 diff 裁掉,DSH 下一轮重建系统段时 就会消失。

sync_rules

messages 只有固定组,fixed_message_count === messages.length。 有 session_id 就更新该会话;没有就创建并绑定一个空会话。 该操作不执行模型生成,返回 choices: [],也不发回上一轮图片规划。 固定规则按 type 整组替换,不会让旧规范和新规范并列生效。

reset

messages 为空,fixed_message_count: 0,fixed_message_types: []。 创建新的空 Agent 会话并返回新的 session_id;旧会话保留在 DSH 历史中。 下次 generate 携带固定组和动态组重新初始化。

响应

生成成功:

{
  "id":"chatcmpl-example",
  "object":"chat.completion",
  "choices":[{"index":0,"message":{"role":"assistant","content":"Agent 最终回复字符串"},"finish_reason":"stop"}],
  "dsh_session":{
    "client_id":"baibai-image",
    "chat_id":"stable-tavern-chat-identity",
    "task_id":"default",
    "session_id":"session-st-..."
  },
  "bridge":{"client_id":"baibai-image","chat_id":"stable-tavern-chat-identity","task_id":"default","session_id":"session-st-..."},
  "usage":{"prompt_tokens":10000,"completion_tokens":1200,"prompt_tokens_details":{"cached_tokens":8000}}
}

控制操作成功:

{
  "choices":[],
  "dsh_session":{"client_id":"baibai-image","chat_id":"...","task_id":"default","session_id":"session-st-..."}
}

sync_rules 和 reset 只有在会话状态真正落实后才返回成功。usage 只在 真实模型调用产生时出现;控制操作没有 usage。

文本能力与工具模式

纯文本、JSON 字符串和填表文本指令保留原有行为。桥接 0.12.0 与酒馆扩展 0.4.4 增加客户端 tools: [{type:"function",function:{...}}]、assistant.tool_calls 和 role:"tool" 的原生往返,参见 外部工具接入。 需要 stream:false,tool_choice 省略或为 auto,parallel_tool_calls 省略或为 false;旧 functions/function_call、强制工具选择、strict:true、并行和工具流式 仍明确拒绝,不会静默降级成普通正文。

response_format: {"type":"json_schema",...} 这种严格结构约束返回 400 unsupported_response_format;json_object 可以按普通文本处理,但桥接 不承诺严格 schema 采样。DSH Agent 内部自用工具与调用端 OpenAI 工具协议不是 同一件事。

能力探测

GET /v1/bridge/capabilities

返回协议版本、范围键、控制操作、文本/工具/严格 JSON/流式能力,不创建会话。 连接测试不应调用 generate,避免留下正式任务会话。

{
  "object": "bridge.capabilities",
  "protocol_versions": [1],
  "scope_keys": ["client_id", "chat_id", "task_id"],
  "control_actions": ["generate", "sync_rules", "reset"],
  "input_modes": ["text", "json", "form_text"],
  "tool_calls": true,
  "external_tools": {"streaming":false,"execution":"caller","tool_choice":["auto"],"parallel_execution":false},
  "strict_json": false,
  "streaming": false
}

重试、隔离与 410

  • 同一范围下的同一 request_id 只执行/追加一次;重复请求返回同一结果。
  • 首次生成、sync_rules 创建、reset 使用由 client_id + chat_id + task_id + request_id 派生的稳定 session ID; 进程崩溃后重试会续用同一会话,而不是再建一个。
  • 不同范围(插件 / 聊天 / 子任务)严格隔离;session_id 不属于当前范围时 返回 410 session_gone。
  • 未知/失效的 session_id 也返回 410;插件清除本地绑定后重新发送固定组。
  • 同一范围的请求串行处理;跨标签页并发也会排队。
  • 普通文本的 stream: true 在本协议下被忽略;外部工具模式要求 stream:false。
  • target 只作为元数据保存,不作为正文、画面内容或提示词注入。

普通 OpenAI 兼容模式(旧版/备用,默认关闭)

不传 dsh_session 时,默认返回 400 persistent_protocol_required,避免一次 健康检查/测试请求在 DSH 里创建一个正式持久会话。确实需要旧模式时,在桥接 配置中显式设置 allowLegacyPlain: true;启用后按普通 OpenAI 消息模式处理, 每次生成请求必须带两个稳定标识:

字段 含义 可放位置
chat_id 酒馆聊天 ID,稳定不变 body.chat_id、body.chatId、body.metadata.chat_id、x-chat-id
request_id 本轮请求 ID,重试时不变 body.request_id、body.requestId、body.metadata.request_id、x-request-id、Idempotency-Key

同一 chat_id 的请求会串行处理。不同 chat_id 可以并行,互不串台。

首次请求

把完整规范、角色资料、世界书、已有上下文和目标正文按顺序放进 messages:

curl -s http://127.0.0.1:43121/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{
    "model": "dsh-default",
    "chat_id": "tavern-chat-001",
    "request_id": "turn-0001",
    "messages": [
      {"role": "system", "content": "完整规范/世界书/角色资料"},
      {"role": "user", "content": "已有剧情上下文"},
      {"role": "assistant", "content": "上一段正文"},
      {"role": "user", "content": "本轮目标正文"}
    ],
    "temperature": 0.6,
    "max_tokens": 8192,
    "stream": false
  }'

桥接会创建 DSH 会话,发送完整上下文,并在成功后把最终回答写入 choices[0].message.content。

后续请求

推荐继续发送 完整 messages 历史(OpenAI 常见做法),桥接会自动只追加 上次已发送内容之后的新增部分:

{
  "chat_id": "tavern-chat-001",
  "request_id": "turn-0002",
  "messages": [
    {"role": "system", "content": "完整规范/世界书/角色资料"},
    {"role": "user", "content": "已有剧情上下文"},
    {"role": "assistant", "content": "上一段正文"},
    {"role": "user", "content": "本轮目标正文"},
    {"role": "assistant", "content": "桥接上一轮返回的正文"},
    {"role": "user", "content": "本轮新增正文"}
  ]
}

也可以只发送新增内容,桥接会把无 system 的 messages 当作增量:

{
  "chat_id": "tavern-chat-001",
  "request_id": "turn-0003",
  "messages": [{"role": "user", "content": "只发本轮新增正文"}]
}

如果只发增量,建议显式加 "context_mode": "delta",避免客户端某些特殊格式被误判。

调参

  • temperature、top_p 等采样参数当前版本不会透传到 DSH Agent(安全忽略)。
  • max_tokens 会作为 agentOptions.maxTokens 传给新会话/恢复的会话。
  • stream: true 会返回 OpenAI 风格 SSE,但内容在 Agent 完成整轮后一次性发出; 不是逐 token 流。
  • 其他未知字段安全忽略。

API

GET /v1/models

固定返回:

{
  "object": "list",
  "data": [{"id": "dsh-default", "object": "model"}]
}

dsh-default 映射到 DSH 当前选择的模型通道;如果桥接配置里写了 provider/model,则使用配置值。

POST /v1/chat/completions

请求体:

{
  "model": "dsh-default",
  "chat_id": "tavern-chat-001",
  "request_id": "turn-0001",
  "messages": [
    {"role": "system", "content": "..."},
    {"role": "user", "content": "..."}
  ],
  "context_mode": "auto",
  "max_tokens": 8192,
  "stream": false
}

context_mode:

值 行为
auto(默认) 完整历史做前缀 diff;无 system 的列表优先按增量处理
full 强制按完整历史 diff;重写/回退会触发重建
delta 强制把 messages 全部当新增内容追加
branch 强制重建分支会话,用本次 messages 作为新分支完整上下文

成功响应:

{
  "id": "chatcmpl-turn-0001",
  "object": "chat.completion",
  "created": 1793054822,
  "model": "dsh-default",
  "choices": [
    {
      "index": 0,
      "message": {"role": "assistant", "content": "最终正文或 images/changes JSON"},
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1234,
    "completion_tokens": 567,
    "prompt_cache_hit_tokens": 100
  },
  "bridge": {
    "chat_id": "tavern-chat-001",
    "session_id": "session-...",
    "turn": 12,
    "idempotent": false,
    "branch": false
  }
}

usage 只在 DSH/上游真实提供时出现。缺失的字段直接省略,不伪造成 0。 缓存命中可能是 prompt_cache_hit_tokens,也可能是 prompt_tokens_details.cached_tokens,两者都按上游原样透传。

新请求还会在 usage.diagnostics 中记录诊断;usage 字段结构和跨事件累加方式保持兼容,原生 DSH 输入口径按下述账目规则纠正:

  • usageEventCount 是带 usage 的消息事件数,maxUsageEventPromptTokens 是事件最大输入,二者不等同于模型调用数和单次上下文。
  • 每个事件有明确的 data.callId 或合法的 turn:step 时才提供 modelCallCount,消息 ID 不作替代。同一步重复记录时不猜测 token 去重方式。
  • 只有调用 ID 全部唯一且每条均有输入计数时,才提供 sumPromptTokens 和 maxPromptTokens。重复调用 ID 的 usage 是快照还是增量无法确认时,保留未知。
  • cacheReporting 标记是否提供缓存数据,cacheCoverage 为 complete/partial/none。完整且独立的调用才提供诊断缓存合计;缺失值不补零。
  • usageRecords 保留调用标识、识别到的数值 usage,以及原始 DSH 数值字段 adapterUsage,不复制正文、请求头或任意上游元数据。两种缓存字段若同时存在,不相加;诊断合计优先使用 prompt_cache_hit_tokens。

DSH 的 cacheReadTokens 映射到 prompt_tokens_details.cached_tokens。若原生字段满足 totalTokens = inputTokens + cacheReadTokens + cacheWriteTokens + outputTokens,则按已核对的账目将缓存部分计入 OpenAI 格式的 prompt_tokens;已有显式 prompt_tokens 不再加一次缓存。方程无法验证时保留原输入值,不能只因缓存读取大于该值就断言数据累计或上下文截断。旧版本保存的 prompt_tokens 可能只含未缓存部分,不能直接与新版本比较。

只读账本(不会调用模型、修改状态或压缩历史):

node tools/ctx-audit.mjs --state /path/to/state.json
node tools/ctx-audit.mjs --state /path/to/state.json --json /path/to/report.json

调用数与事件数分列;历史无诊断时显示未知。--prompt /path/to/payload.json 可提供按正则分类的估算,仅用于观察方向,不能当作准确来源归因。固定规则的字符体积来自当前保存版本,token 数为估算,不代表旧请求的最终模型输入。源仓库更新后,需在没有生成任务时另行同步 vendor 并重启;旧记录不会自动补齐。

流式响应:

curl -N http://127.0.0.1:43121/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{
    "chat_id": "c1",
    "request_id": "r1",
    "stream": true,
    "messages": [{"role": "user", "content": "hello"}]
  }'

返回标准 data: {...} 分片,最后 data: [DONE]。若 stream_options.include_usage 为 true,会在结束前发一个 usage 分片。

POST /v1/bridge/reset

重置某个聊天的绑定。旧 DSH 会话保留在历史里,不会被删除;下一次请求创建新会话。

curl -s http://127.0.0.1:43121/v1/bridge/reset \
  -H 'content-type: application/json' \
  -d '{"chat_id":"tavern-chat-001"}'

当前聊天还有请求未结束时返回 409 chat_busy。

GET /v1/bridge/status?chat_id=...

查看绑定、会话 ID、模型、预设、已记录消息数、历史分支。

GET /health

健康检查,返回服务名、版本、端口、启动时长。

普通请求兼容模式

有些插件(例如玉子手机的 API 预设)只能填 URL / Key / 模型,无法发送 dsh_session。桥接默认拒绝这类请求;如果在设置面板显式开启兼容模式,可以把 它们归入一个固定的本机 scope:

plainCompat.enabled = true
plainCompat.clientId = yuzi-phone
plainCompat.chatId   = yuzi-phone
plainCompat.taskId   = default

行为:

  • 只对不带 dsh_session 的 POST /v1/chat/completions 生效;
  • 自动补 protocol_version:1、action:generate、fixed_message_count:0, 并按 chat_id + messages 生成稳定的 request_id;
  • 所有普通请求共用这一个持久会话;
  • stream: true 会返回 SSE,其余返回普通 JSON;
  • 显式带 dsh_session 的请求不受影响。

这是全局开关,同一时间只适合一个不会发 dsh_session 的插件。数据库有能力发 dsh_session(用固定组),不要和玉子共用兼容模式。

行为细节

首次 vs 后续

  • 首次请求:messages 全部进入新会话。最后一条是 user 时,它被放到 <st_bridge_current_request>;其他消息按原顺序放进 <st_bridge_context>。
  • 后续请求:如果完整历史是已记录历史的严格扩展,只把新增的 user/system 消息发进同一会话;已发送过的内容不会重复追加。
  • 单条新增 user 消息会原样发送,不加包装,尽量保持柏宝绘的原文和格式。

request_id 幂等

  • 同一 chat_id + 同一 request_id + 相同 messages:直接返回上一次结果, 不新增消息、不重复调用模型。
  • 同一 request_id 但 messages 不同:409 request_id_conflict。
  • 同一 request_id 正在处理:等待同一个 Promise;跨进程重启用会话日志尝试恢复。
  • 失败请求会缓存错误;如需重试,请换新的 request_id。

分支/重建

以下情况会创建 新的 DSH 会话,旧会话保留:

  • 旧楼层被改写(messages 与已记录历史在中间分叉);
  • 回退剧情(messages 变短);
  • swipe/重新生成(相同或截断的上下文,没有新增用户内容);
  • 固定规范、世界书、角色资料变化;
  • 原会话无法恢复且本次带完整上下文。

新会话会重新接收完整上下文,避免旧场景污染新分支。分支关系记录在 previous_sessions 中。当前版本采用“重建”而不是 DSH 原生 fork;这满足 “从对应位置分支或重建会话”的要求,也最容易保证干净。

串行

同一 chat_id 的请求进入单条 Promise 队列;上一轮完成后才开始下一轮。 不同 chat_id 各自独立队列。

最终回答

只取本轮最后一条不含 tool-call 的 assistant/message。工具调用步骤、进度 提示、内部思考不会混进最终回答。回答原样返回,不解析 JSON,不加代码围栏。

DSH 界面继续交流

会话是普通 DSH 会话,会挂到 cwd 对应的 MOSS 工作区。你可以在 DSH 里打开 它继续聊天。桥接的增量 diff 只跟踪柏宝绘发来的消息,不跟踪你在 DSH 界面 手工输入的消息;但那些消息已经在同一个会话里,后续桥接追加时 Agent 仍能 看到它们。只有在触发分支/重建时,才会只使用柏宝绘提供的上下文。

配置

安装脚本写入的配置块:

- id: st-bridge
  name: ST-bridge
  config:
    host: '127.0.0.1'
    port: 43121
    cwd: 'D:\AIGC\MOSS'
    agentPreset: st-bridge
    requestTimeoutMs: 1800000
    maxBodyBytes: 33554432

可选字段:

字段 默认 说明
provider / model 空 旧版全局字段,新会话不再读取;请在插件接入条目选择模型
maxTokens 空 旧版全局字段,新会话不再读取;请在插件接入条目设置
agentPreset st-bridge 旧版兜底;新会话优先使用插件接入条目的预设,留空跟随 DSH 默认
clientEndpoints[].provider / .model 空 插件专属模型,必须同时填写或同时留空
clientEndpoints[].agentPreset 空 插件专属 Agent 预设,留空跟随 DSH 默认
clientEndpoints[].cwd 空 插件专属工作区,留空使用 DSH 默认工作区
clientEndpoints[].maxTokens 空 插件专属输出上限,请求里的 max_tokens 优先
clientEndpoints[].persistentSession false 该插件所有聊天共用一个 DSH 窗口;切换开关会清掉旧窗口,关闭后各聊天从新会话开始
authToken 空 设置后要求 Authorization: Bearer 或 x-api-key
corsOrigins 未设置 额外允许的 Origin;默认允许本机与浏览器扩展
allowRemote false 允许绑定非 127.0.0.1 地址,不建议
allowLegacyPlain false 允许不带 dsh_session 的旧式普通补全;会创建持久会话
allowedClientIds [] 非空时只允许列表中的 client_id;留空允许所有
stateDir ~/.dsh/st-bridge 映射和日志目录

状态文件:~/.dsh/st-bridge/state.json。日志: ~/.dsh/st-bridge/bridge.log。

错误

所有错误都是:

{"error":{"message":"具体原因"}}
HTTP code 含义
400 missing_chat_id / missing_request_id 缺少稳定标识
400 model_not_found 只支持 dsh-default,或当前插件接入条目选择的模型 ID
400 unsupported_content 消息含非文本片段,拒绝静默丢弃
400 persistent_protocol_required 未带 dsh_session 且未开启旧模式
400 invalid_client_id / invalid_task_id 插件身份或子任务标识不合法
400 unsupported_tool_mode 调用了原生工具协议
400 unsupported_response_format 请求了严格 JSON schema 约束
400 unsupported_reasoning_effort 模型不支持该思考强度;桥接会按模型能力过滤
400 invalid_fixed_count / fixed_types_mismatch / invalid_fixed_type 固定组字段不合法
400 reset_messages_not_empty / empty_dynamic_group / missing_user_message action 与 messages 不匹配
401 unauthorized 配置了 token 但请求未带/带错
403 origin_forbidden 浏览器 Origin 不在白名单
403 client_not_allowed client_id 不在面板允许列表中
409 request_id_conflict 同一 ID 对应不同请求内容
409 request_in_progress 该 ID 仍在处理,且无法从日志恢复
409 duplicate_context 没有新增内容
409 session_unavailable 会话恢复失败且本次只有增量
410 session_gone 会话未知、失效或不属于该 chat_id;插件应清绑定并重新初始化
413 context_length_exceeded 上下文超出模型限制
413 body_too_large 请求体超过上限
502 agent_turn_error Agent 回合失败
502 upstream_account_error 账号/额度异常
502 empty_answer 回合结束但没有最终正文
504 timeout 超过 requestTimeoutMs

安全

  • 默认只绑定 127.0.0.1。
  • 默认允许本机页面和浏览器扩展 Origin;其他网页 Origin 返回 403。
  • 如果本机还有其他用户或不可信页面,请设置 authToken。
  • 桥接会让本机调用方使用你的 DSH 模型账号并可能产生费用。

发布到 GitHub

本目录已经初始化为本地 Git 仓库,当前分支 main,未配置 remote,也没有执行推送。 创建好 GitHub 仓库后:

git remote add origin https://github.com/<你的账号>/<仓库名>.git
git push -u origin main
git push --tags

提交作者由本机 Git 身份决定。后续提交如需使用自己的身份:

git config user.name "Your Name"
git config user.email "you@example.com"

仓库里不要提交 settings.json、state.json、*.log;.gitignore 已排除。

blocked 与归档会话

桥接会在恢复会话时检查 DSH 的归档状态。默认返回 HTTP 409、error.code=session_archived,可恢复旧会话或清除绑定后新建。配置 sessionOnArchived: recreate 时,生成请求会自动新建替代会话;0.16.3 起,sync_rules 协议同步也遵循该配置,覆盖专属模式显式携带旧 session_id 和从当前绑定复用旧会话两条路径。替代会话创建成功后才更新绑定并记录旧会话,固定协议重新载入;旧会话保持归档。此兜底不处理其他类型的 409 冲突。

DSH 在模型调用前拒绝请求时,日志可能只有 inbox 领取记录和 turn/end: blocked,没有 user/message。桥接会按确切 request ID 回查被领取的 inbox 消息,将该请求标记为 error 并返回 agent_turn_blocked,不再等待整个超时时间。重启后重试旧 pending 请求时也使用同一恢复逻辑;不匹配的、尚未领取的和取消的消息不会被当成本请求的结果。

0.16.4 起,DSH 在领取前取消 inbox 消息时,桥接按 request ID 返回 HTTP 499、agent_request_canceled,及时结束等待并释放同一会话的队列,后续 sync_rules 不再被该请求拖到超时。取消的请求不会自动重发或借用其他回合的回答;同一消息移到另一个 inbox 后继续执行时,仍等待自己的结果。

HTTP 400 persistent_protocol_required 是另一类入口错误:请求未携带 dsh_session,尚未进入 Agent 回合。应修正调用方的协议配置;它不等于 blocked。

测试

cd D:\AIGC\MOSS\ST-bridge
npm test

测试覆盖:dsh_session 的 generate/sync_rules/reset、固定组注入、多插件/子任务 scope 隔离、旧绑定兼容、工具模式拒绝、capabilities、410、request_id 幂等、 串行、usage 透传;设置面板的设置校验、持久化、token 不回显、危险操作确认; 以及旧模式的 messages 顺序、多 system、前缀 diff、增量/分支/回退、SSE、 工具调用过滤。

柏宝绘专属模式(可选)

柏宝绘可使用插件级稳定 chat_id,并发送 session_mode: "dedicated" 和 source_chat_id。 普通模式保持原样。专属模式的 sync_rules/reset 成功响应在验证原生 surface API 后返回 dedicated-v1 能力确认。 每次生成前,桥接按 systemSnapshot.mode 维护受保护的系统头(默认 native,不再写入空节点),并替换历史会话尾部;旧事件仍留在不可变日志中。新生成使用本轮动态快照、当前有效固定规则及已确认的会话版式图册。 快照差量裁剪在该模式下停用。相同 request_id 继续幂等,来源聊天改变会触发请求冲突。 会话标题稳定为「柏宝绘专属会话 · 输出格式」,不跟随正文变化。

图片目录可通过 dsh_session.layout_atlas = {"hash":"<64位小写SHA-256版本标识>","message_indices":[...]} 绑定为会话前缀。首轮索引指向本请求中携图的动态 user 消息,桥接独立保存为原生图册节点;后续只发送相同 hash 和空索引数组,不再上传图册。冷却仅通过本轮文字白名单更新,不改变完整版式图。图册内容或版式几何变化时传新版本及完整图片,桥接替换旧前缀;重建、失效或压缩导致图册不可见时返回 410 layout_atlas_required,客户端须重新发送。图册只在当前 client/chat/task/session 中复用,不跨会话借用。能力接口通过 layout_atlas.mode: "session-prefix-v1" 声明支持,成功响应回显 dsh_session.layout_atlas_hash。

管理员可在 clientEndpoints[].skillId 绑定本地 Skill;请求中的 dsh_session.skill_id 仅作绑定校验,响应回显实际生效的名称与内容哈希。Skill 须安装在该预设可读取的目录(如 ~/.dsh/skills)。0.15.0 起每个新请求读取最新正文并替换唯一的 st-bridge:skill 系统规则块,更新内容无需重置会话;同一 request_id 的重试使用首次接受的版本,不能借重试更换正文或 Skill。设置 dsh_session.skill_enabled:false 可关闭本次 Skill 注入;缺省继续跟随管理员绑定。当前仅注入 SKILL.md 正文,不自动加载 references 或脚本。不要将外部工具续接与专属快照模式混用。

—/ 5

No ratings yet

Verified DSH bundle

Commit b63fb30594ab

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout