# 鲸灵 Jingling:架构与 DSH 兼容边界 > 源码核对基线:DeepSeek Harness `47f943859bef60e4160492346772ded9b24f765a`(当时包版本 `0.1.0-rc.5`);发布依赖目标已于 2026-08-14 更新到公共 npm `0.1.0-rc.6`,并完成下述 rc.6 真实集成验证。 > **公开包范围:** `dsh-jingling` 只携带 DSH 插件、文档和 `assets/jingling-idle.png`。本文涉及的 Electron Sidecar 是独立开发候选;其二进制、角色、模型、声音和动画素材均不随此 npm 包或本公开仓库分发。 ## 1. 包结构 鲸灵是同一个 npm 包中的三个边界: - `dsh-jingling`:根 host 插件,负责 storage-domain、粗粒度 presence、loopback RPC 和主动问候调度; - `dsh-jingling/agent`:只挂进 `souldock` preset,负责系统契约、动态人格 / 记忆上下文、陪伴工具和 monotonic guard; - `dsh-jingling/client`:Web 客户端模块,在 `conversation.view` 注册原生页面。 包根必须作为启用的 Loader row 出现在 `cordis.patch.yml`。DSH 客户端扫描器只从活跃的包根读取 `dsh.client` 与 `exports["./client"]`;只加载 `/agent` 子路径无法发现 UI。 客户端构建产物不是普通 ESM,而是 `window.__ModuleLoader__.load({ id, factory })` 包装的闭包 bundle。`scripts/build.mjs` 在 Windows 上使用 `fileURLToPath`,避免盘符路径被 URL pathname 破坏。 ## 2. 持久状态 跨会话状态使用 `ctx.storageDomain.open()` 的 global document。它是本机单用户全局作用域,只按 `personaId` 隔离,尚无独立用户 / 项目命名空间: - persona 列表与当前 persona; - 按 `personaId` 隔离的 active / superseded memories; - 已确认记忆;未确认 proposals 只保留在当前进程内,不属于 durable document; - 关系、主动问候、语音与隐私设置; - 不含已遗忘正文的有界 audit; - 有界 proactive dispatch history。 每次写入在 service promise resolve 前完成。遗忘会删除目标正文和被该版本取代的旧正文;audit 只保留 opaque id。 鲸灵不创建 `souldock/*` Session 事件。所核源码的 known-event-types 是闭集,第三方插件写入未知且非 ignorable 的持久事件可能导致重启读取失败;公开 `Session.append()` 也没有给树外普通事件设置 `ignorable: true` 的入口。`systemPrompt.context()` 只注入人格、关系、边界和记忆检索规则;标准记忆正文由模型按需调用 `companion_memory_recall` 获取,分类为私密的记忆从模型工具层硬排除,UI 卡片走独立 storage + RPC。普通模型提议只在 service 进程内保留最多 24 小时,确认前不写 storage-domain;凭据和内置规则命中的常见敏感模式会拒绝,但规则不是完整 DLP,必须由用户逐条审阅。旧版 durable proposals 在初始化时全部清理;旧版 confirmed memories 重新分类,未知敏感级别默认私密,凭据样式条目 fail closed 删除并持久写回。 ## 3. 权限边界 专用 preset 不组合 shell、filesystem、web、MCP、skills、subagents、workflow 或 Code Mode。`ctx.tools.guard()` 再使用 allowlist 拒绝任何后来全局注册的工具;DSH guard 在 pre-execute 边界是单调的,后续监听器不能把 deny 改回 allow。 模型允许工具只有普通记忆提议、标准记忆检索、中性 handoff 草稿和隐私安全状态摘要。不存在 schedule 例外;handoff 只生成 `executable:false` 数据。遗忘、角色导入、成人确认和关系更新不注册为模型工具,只能走 loopback UI RPC,因此模型生成 `confirm:true` 没有提交能力。 ## 4. 配置面与 RPC 敏感配置不挂裸 `webServer.register`。鲸灵使用: ```text ctx.connection.rpc.handle('/souldock', handler, { authority: 'loopback' }) ctx.connection.rpc.call('/souldock', endpoint, payload, signal) ``` 这复用 DSH 的请求 envelope、body cap、AbortSignal、Host / Origin / DNS-rebinding 检查与 scope cleanup。应用错误留在成功的 transport envelope 内,只返回简短 message,避免抛出 host stack。 ## 5. 主动问候 运行时只考虑 live root agent,权威判断是: ```text ctx.agents.roots().includes(agent) ctx.agents.get(agent.id) === agent ctx.agentPresets.composedPreset(agent.ctx) === 'souldock' ``` 不能只读 `session.header.agentPreset`,因为空白会话允许重新 compose,而 header 是创建事实。运行时监听 `agent/created`、`agent-preset/selected(sessionId, preset)`,并在启动 / HMR 时扫描 root agents。 用户活动从 agent-scoped `agent/inbox/inserted` 读取,并且只接受 `message.source.kind === 'user'`。恢复时只扫描 `session.firstLiveSeq` 之后的当前进程事件,避免把数月前持久历史当成刚活动。插件自己的主动 trigger 是 `source: { kind:'plugin', plugin:'dsh-jingling' }`,不会延后“最后用户活动”。多个 live 鲸灵 root 只选择当前进程最近有人类活动的一个,不广播。 投递仿照官方 Schedule 的维护边界:先用 `agent.runMaintenance()` 同步认领 idle phase,在 callback 中重新核对 live / preset / policy,通过同一 service mutation 原子复核全局预算并持久化 dispatch reservation,再 `agent.followup(createUserMessage(...))` 和 `sessions.flush()`。busy 同步抛出时在 maintenance 外等待 `agent.whenIdle()` 后重试。Storage 与 Session inbox 无跨域事务,所以这里明确选择 at-most-once:极端崩溃可能消耗一次额度但不发出问候,不能重复骚扰。它只在 DSH 进程和该 Session 存活时运行,也不证明浏览器在线、用户已读或模型成功。 ## 6. UI 与桌面边界 所核源码有 `conversation.view`、header / composer dock 等会话内挂载位,但没有可假定的全局 floating overlay slot。因此跨页面透明桌宠使用独立 Electron sidecar,不把 Electron、模型或桌面生命周期塞进 DSH 插件进程。选择 Electron 是为了直接沿用 AIRI 已验证的透明、置顶、跳过任务栏和鼠标穿透窗口模式,以及其 Vue / Three / Pixi 生态;本机已有的 AIRI 工作树包含未提交改动,绝不作为发布源,实际复用必须固定官方 commit/tag 并保留 MIT 声明。 DSH host 的 presence 面只暴露两个 Connection loopback RPC: - `sidecar/status`:角色 `id/name`、当前粗粒度 runtime、序号与能力声明; - `sidecar/poll`:最多等待 25 秒并返回进程内有界事件流,支持请求 `AbortSignal`。 两者都不返回 memories、proposals、人格 prompt、工具参数或传感器数据,通用 presence 流也不再转发助手正文。 用户点击桌宠后,sidecar 只调用同一 `/souldock` channel 下的 `chat/open`、`chat/send`、`chat/poll`、`chat/cancel`、`chat/close`。Host 内部直接复用 rc.6 `ctx.apiProxy.sessions.list/create/history/prompt/cancel`,但不把通用 `/api` 或聚合所有会话的 WebSocket 下行暴露给 Electron。`chat/open` 只创建 / 恢复 `souldock-sidecar-*`、`agentPreset=souldock`、非 subagent 的专属会话并签发进程内临时 `chatId`;后续请求不接受任意 sessionId。输入固定为 8000 Unicode code points 内的纯文本 queue,拒绝图片、steer、斜杠命令和未知字段。输出只投影真实用户消息、助手 `text-delta` 与最终 text block;reasoning、插件 context、memory ledger、人格 prompt、工具调用 / 结果、审批、问题和附件在序列化前丢弃。收起只撤销 handle,不自动取消 DSH 回合,完成回复留在 DSH history,下一次显式打开时再读取。 “工作区”使用独立的 `workspace/list` 与 `workspace/task/{create,open,send,poll,cancel,close}` BFF。Electron 主进程通过系统目录选择器获得用户明确选择的路径,并只放行本次选择或 DSH 已登记的路径;Host 再调用官方 `workspace.create({path})` 与 `session.create({workspaceId, agentPreset:'standard'})`。只有 `souldock-workspace-`、`standard`、非 subagent 的根任务可被列出或打开。DSH workspaceId、sessionId 和消息 ID 在跨进程前映射成稳定的不透明别名;输出仍只有可见用户/助手文本与粗粒度状态。审批和问题只投影为 `needs-input`,原始理由、工具、参数与问题正文留在 DSH 中处理。工作区不会注入陪伴人格或记忆。 Connection 的 `authority: loopback` 是 Host / Origin / DNS-rebinding 围栏,不是认证层;部署仍必须把 DSH 绑定到 `127.0.0.1`。VRM 模型由用户导入并检查 VRM meta;Live2D 只保留可选 adapter,不随包分发 Cubism Core、示例模型或来源不清的角色资产。 ## 7. rc.6 验证记录与剩余边界 - 已在隔离的真实 `0.1.0-rc.6` profile 完成插件安装、完整 Web 插件树启动、首页与 loader 包装客户端 bundle、loopback RPC、状态读取、记忆创建 / 重启持久化 / 遗忘,以及 `souldock` preset Agent 组装; - 已在用户 `web` profile 安装并于 `127.0.0.1:3080` 加载,浏览器可选择“鲸灵 · Jingling”; - 已用真实 rc.6 `session.prompt` 接收 sidecar 纯文本并验证失败事件投影,但当前 DSH 缺少 DeepSeek 模型密钥,因而尚未取得模型最终回复;模型驱动工具红队与真实扬声器 TTS 仍未验证; - 还未完成真实危机语料、未成年人和情感操纵输出的双语红队认证; - DSH 侧窄化 sidecar 状态 / 长轮询 / chat / workspace BFF 和自动测试已经完成;真实 rc.6 profile 已验证工作区任务创建、列表、纯文本发送、状态轮询、关闭和归档。独立 Electron 壳的开发验证记录与本 npm 包分离;本公开包不携带其二进制、角色、动画、VRM、Live2D 或声音素材,也不以本包声明这些素材的分发权。系统 SpeechSynthesis 与可选本机 VOICEVOX 只保留独立适配边界;真实扬声器播放仍未验收,Live2D 仍只有不加载任意 runtime.js 的 provider seam; - 当前没有自动 Worker handoff、麦克风或视觉输入,也没有可声称的全双工语音。 这些边界必须在任何 release note 中继续保留,直到有相应可复现实验。