🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

日语翻译模式 wangjiezhe/dsh-jp-translate

DeepSeek Harness
wangjiezhe31 天前有提交已提供中文文档扫描:未发现敏感能力

只做日译中,不带工具与历史消息

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/1 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add wangjiezhe/dsh-jp-translate
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · skill
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 31 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包@workspace/dsh-jp-translate(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖要求 >=0.2.0-rc.1 · 最新 0.2.1-rc.2 兼容
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/20 04:30:19

安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 仅日译中
  • 无工具调用
  • 无历史消息

「日语翻译」模式

README

由 DeepSeek 最新模型翻译生成
日语翻译 (dsh-jp-translate) 一个比「极简模式」还简洁的 DSH agent 预设:只做日译中,不加载任何工具,并且每次请求只发送 system_prompt 与 user_prompt,不携带任何历史消息。 这个模式做了什么 | 事实 | 实现 | | 系统提示词完全可控 | 预设内插件注册一段 complete: true 的提示词段,因此组装后的系统提示词就是 [Task] / [Tone] / [Strict Rules] / [Glossaries] / [Source Text] 这一段,不会掺入 harness 身份、部署 persona、工具指引、工作区指令或运行时上下文 | | 不加载任何工具 | 预设只声明一个 preset-local 插件行,没有任何 shell / 文件 / 搜索 / 子代理 / skill 行,所以请求的 tool 清单为空 | | 不带任何历史消息 | 每次请求前用一条空的 developer/message 做位置替换,遮蔽除系统提示词(surface node 0)以外的全部表面节点;钩子写在 agent/request(step/start 之后、派生请求之前),循环紧接着才派生请求,因此线上消息恒为 [system] 提示词 + [user] 本次原文。
展开完整文档(README 剩余 1 节)
日语翻译 (dsh-jp-translate)

一个比「极简模式」还简洁的 DSH agent 预设:只做日译中,不加载任何工具,并且每次请求只发送 system_prompt 与 user_prompt,不携带任何历史消息。

这个模式做了什么

| 事实 | 实现 |
| 系统提示词完全可控 | 预设内插件注册一段 complete: true 的提示词段,因此组装后的系统提示词就是 [Task] / [Tone] / [Strict Rules] / [Glossaries] / [Source Text] 这一段,不会掺入 harness 身份、部署 persona、工具指引、工作区指令或运行时上下文 |
| 不加载任何工具 | 预设只声明一个 preset-local 插件行,没有任何 shell / 文件 / 搜索 / 子代理 / skill 行,所以请求的 tool 清单为空 |
| 不带任何历史消息 | 每次请求前用一条空的 developer/message 做位置替换,遮蔽除系统提示词(surface node 0)以外的全部表面节点;钩子写在 agent/request(step/start 之后、派生请求之前),循环紧接着才派生请求,因此线上消息恒为 [system] 提示词 + [user] 本次原文。替换是持久的,会话恢复后同样成立;事件日志仍然只增不删 |
| 术语表可编辑 | 设置页「日语翻译」:预设选择器 + 「原始文本」「翻译文本」两个文本框,按行一一对应 |
| 可保存为预设 | 新建 / 复制 / 删除 / 命名,保存后写入该插件 profile entry 的唯一 volatile 配置字段,host 随即用新术语表重新声明 agent 预设 |

安装

在 DSH GUI 里用插件管理器安装本目录(pnpm file: 依赖 + profile bundle patch):
plugin_manager(install_bundle, target = "file:")
或使用 dsh CLI:
dsh plugin --profile desktop add "file:C:\path\to\dsh-jp-translate"

安装后 profile 的 package.json 会多出 dsh.profile.bundles 一项,cordis.patch.yml 里的 jp-translate 行由本包的 cordis.patch.yml 插入。新会话的预设选择器里即可看到「日语翻译」。

术语表存放位置

术语表整体存放在该 profile entry 的 glossary 字段(一段 JSON 字符串):

{"version":1,"active":"default","presets":[{"id":"default","name":"默认","source":"仁王立ち\n気をつけ","target":"挺胸站立的姿势\n立正"}]}

出厂默认即本模式规格里的四条:

仁王立ち → 挺胸站立的姿势気をつけ → 立正ピンセット → 镊子トーズ → 苏格兰皮拍

两个文本框按行索引对应:第 1 行原文配第 1 行译文;空行被忽略;原文有而译文空的词条会
单独列出(仍会写进 [Glossaries])。译文多出的行没有对应原文,不会写入。

目录结构

package.json dsh.bundle.patch + dsh.client 声明 cordis.patch.yml 插入 host 行 jp-translate node_modules/ 本包自己的依赖(@deepseek-ai/schemastery + 其 ESM 依赖 cosmokit) lib/state.js 术语表模型 + 提示词模板 + 设置值编解码(host / 浏览器共用逻辑) lib/preset.js 预设声明(id=jp-translate,name=日语翻译,唯一 plugin 行) lib/shadow.js 无历史 / 无运行时上下文的不变式(可独立测试) lib/index.js host 半:Config schema、声明预设、设置写入后重声明 presets/jp-translate.mjs 预设内插件:complete 系统提示词段 + agent/request 遮蔽 client.js 浏览器半:设置页(两个文本框 + 预设管理),手写 bundle

七条必须记住的插件契约

1. Config 必须是 schemastery schema,不能是普通对象字面量。
cordis 通过 runtime.Config['~standard'].validate(config) 解析插件配置;普通对象会被当成「默认配置值」而不是 schema,激活时报 Cannot read properties of undefined (reading 'validate')。
tools/dsh-env/run-preset.mjs 的 resolveLikeLoader() 专门复现这条规则,两个插件半的 Config 都要经过它。

2. 预设内插件(presets/.mjs)的裸 import 必须能从本包自身解析出来。
预设声明里只有包名,没有依赖清单,所以本包自带 node_modules/(schemastery 及其 ESM 依赖 cosmokit),与安装后 profile 的 node_modules 解析路径一致。

3. 遮蔽事件必须带上当前打开的 turn / step,而且必须在 step/start 之后写。
developer/message / system/message 是 Step 作用域的表面事件, @deepseek-ai/dsh-session/invariant 会校验 event.data.turn/step 是否等于当前打开的 turn/step(requireOpenStep)。漏掉会判整轮失败,报 developer/message turn must be a non-negative safe integer——第二轮才触发,因为第一轮还没有历史可遮蔽。

位置同样是硬约束,而且线上写日志时没人拦你。 agent loop 的顺序是 agent/pre-step → session.append("step/start") → step(),所以在 pre-step 里写这条记录,它会落在 turn/start 与 step/start 之间——那个位置上没有任何打开的 step,任何 turn/step 都对不上。写入照样成功(session.append 只校验表面元数据),于是每一轮都正常,直到重新打开会话时才由已发布的 format-v4 读取器(dsh-session-format-v3-to-v4 的 Relationships.requireStep)整份拒绝:

SessionFormatError: developer/message does not match an open turn and step
→ stored session "session-…" is corrupt(gateway/internal)

这就是 2026-09-30 那次事故:6 个会话、23 行遮蔽,全部再也打不开。修正后遮蔽写在 agent/request——loop 在 step() 里、step/start 之后、派生请求之前派发它
(prepareRequest()),既在打开的 step 内,又是拿到请求之前的最后一个钩子:

ctx.effect(() => ctx.on('agent/request', ({ agent, signal, turn, step }, next) => {
if (typeof agent === 'object' && agent !== null) {
const key = ${String(turn)}:${String(step)}
if (shadowedStep.get(agent) !== key) { // 每个 (turn, step) 只遮蔽一次
shadowedStep.set(agent, key)
shadowHistory(agent, signal, randomUUID, turn, step)
}
}
return next()
}), 'jp-translate: stateless requests')

shadowedStep 这张台账(WeakMap,按 agent 键)不是优化而是正确性:请求重试会带着本轮已经落到表面上的 user 消息再次进入 prepareRequest,再遮蔽一次会把它一起盖掉,重试请求会一条输入都没有。

四条规则现在都由真实读取器钉住(不再是自己抄一份规则的 mirror):

ok every turn of a real loop sequence sends the system prompt and the newest source text only
ok the shadow reaches a resumed session too: a log with history still sends one message
ok the pre-step placement is what corrupts the log — the shipped reader refuses it ok a shadow naming a stale step is refused by the shipped reader ok the shadow message is admitted by the real format-v4 codec ok the retired generic "plugin" source kind is refused — the runtime defect ok a shadow missing its turn/step is refused by the format-v4 codec too

run-spec.mjs 的 loopTurn() 按 loop 的真实顺序逐行写日志(turn/start → step/start → 遮蔽 → system/message → user/message → request/header → assistant/message → step/end → turn/end),再用随包发布的
assertReleasedV4Relationships() 判定「这份日志能不能加载」。旧版测试的 mirror 是
对的、错的是摆放顺序(它把 step/start 放在了遮蔽之前),所以它全绿而线上坏——凡是能问真实实现的,就不要自己复述规则。

4. 消息 source.kind 必须是「生产者自有」的,"plugin" 是已废弃写法。
format-v4 编解码器(@deepseek-ai/dsh-session-format-v3-to-v4)在 assertDeveloperMessage 里显式拒绝 kind === "plugin": format v4 developer message requires id, role, content, and a producer-owned source。
生产者要报自己的名字(agent-instructions 用 { kind: "agent-instructions" }),本插件用 SOURCE_KIND = 'jp-translate'。

这条也是事故的一部分:写进日志的每一行都要能过读取器,agent/pre-step 那次修复只顾了「写进去能跑」,没顾「读回来能过」。

5. 「不加载任何工具」需要两步,且 complete 段只管提示词、不管 tools 数组。
complete: true 只让本段成为全部提示词,请求体里的 tools 是另一路: ToolRuntime 在构造时向 ctx.systemPrompt 注册一个 tool provider,装配时把 schema 收进 assembly.tools,再由 dsh-agent-loop 的
buildRequest(config, preparedCall, assembly.tools, …) 放进请求(空数组则整键省略)。
所以预设内插件要同时做两件事:

// 端一:无论别的插件贡献了什么 schema,线上一律为空
ctx.effect(() => ctx.on('system-prompt/assemble', async (_a, _c, next) => {
const assembly = await next()
return assembly.tools.length === 0 ? assembly : { ...assembly, tools: [] }
}), 'jp-translate: no tool declarations')

// 端二:本预设作用域内任何工具都解析不到(模型硬编出工具名也不会执行)
ctx.inject(['tools'], scoped => { scoped.tools.restrict({ allow: [] }) })

两个要点:

- 作用域是「预设 standing scope」,不是进程全局。 dsh-scope 的
bindScopeParent(agentKey, presetKey) 让注册视图沿链向下继承、事件准入沿链
向上扩展,因此同一个进程里别的 agent 工具照旧。tools.restrict() 只在有 scope 的上下文里可用(这正是它要求的 agent.ctx)。
- tools 只能可选注入(ctx.inject(['tools'], …)),不能写进 inject 数组:没编入工具行的部署本来就没工具可抑制,硬注入会让这一行永远 pending。

run-notools.mjs 用真实的 SystemPrompt + ToolRuntime + 真实 scope 链,按 agent loop 的方式寻址装配(assemble({ scope: }))来钉住三条:装上预设后 assembly.tools 为空且 tools.get(name, key) 解析不到;不装预设的对照组仍带全部工具(否则这条测试可能因为「什么都没生效」而假绿);兄弟 agent 不受影响。

一个已知边角:在本改动之前就已存在的旧会话,第一次在新代码下发请求时, dsh-agent-loop.buildRequest 会发现自己记录的 header 里有工具、现在没有了,于是补一条
source.kind = 'tool-registry' 的 tool-removal developer/message(并进入本轮请求)。
这是 harness 自己写的事实变更记录,我们的 pre-step 遮蔽在它之前执行、够不到它;
下一轮起它就被遮蔽了。新建会话永远是干净的 [system][user]。

6. 「不发运行时上下文」是第三路,complete 段和表面遮蔽都够不到它。
请求里多出来的那条 user 消息(Current runtime context. This snapshot supersedes…)
不是提示词段,也不是 surface 上的历史:dsh-agent-loop 自己渲染
joinContextSections(renderContextSections(assembly)),由 RuntimeContextProjection
作为一条额外的 user 消息跟着 claimed 消息一起提交,而且是在 agent/pre-step 瀑布之后才生成——所以表面遮蔽永远追不上它。两端一起做:

// 端一:本作用域不渲染任何动态 context(快照根本不存在)
ctx.effect(() => ctx.systemPrompt.suppressRuntimeContext(), 'jp-translate: no runtime context')

// 端二:把 loop 自己那条消息从决策里摘掉(旧会话日志里已经有快照时靠这条兜底) const decision = await next() return stripRuntimeContext(decision) // 只删 source.kind === 'runtime-context'

为什么之前是「每隔一次」才出现:RuntimeContextProjection.project() 只在
retained.text 与当前快照不同时注入,而 retained 会在快照所在 surface 节点被替换时置为 null(isReplacementSurfaceEvent + sourceEventSeqs 命中)——我们的历史遮蔽每轮都在替换它。于是「注入 → 被遮蔽置 null → 再注入」交替发生。

两个细节:

- suppressRuntimeContext() 的判定是整条 scope 链上有一个抑制者就全抑制
(scopeLayers.some(...)),所以它也能压掉 dsh-sandbox-policy、dsh-user-approval
注册在 agent scope 上的 context;同时它是 scope 级的,别的 agent 不受影响。
- stripRuntimeContext() 只删那一个生产者的消息,claimed 的用户原文(包括被别的插件改写过的版本)一律放行——越权过滤会丢用户正文。

RUNTIME_CONTEXT_SOURCE = 'runtime-context' 是从 dsh-agent-loop 私有常量镜像来的,
run-spec.mjs 会重新读该模块源码断言镜像仍然一致(改名字就红),而不是相信注释。

7. 已经被写坏的日志要用工具修,而且「修」= 删掉自己写的那几行 + 重新编号。
tools/dsh-env/repair-session-logs.mjs 是这次事故的收尾工具,默认只扫描:

tools/dsh-env/repair-session-logs.mjs # 只扫描,不动文件 tools/dsh-env/repair-session-logs.mjs --out # 把修好的副本写到别处 tools/dsh-env/repair-session-logs.mjs --apply # 原地修复(先备份)

它只删「本插件自有、内容为空、且位置非法」的 developer/message;任何别人的位置非法行都会让它拒绝整份文件(unrelated positional defect)。删行之后必须重排 seq(读取器要求 event.seq === index 连续),并把幸存行携带的所有序列引用一起重映射:sourceEventSeqs、surfaceOp.startSeq/endSeq、headerSeq、sourceEventSeq、 messageSeqs、shadowedSeqs/shadowedRange、image/offload 的 targets[].seq、 throughSeq。任何指向被删行的引用都会让重写中止——靠「忘掉这个引用」修好的日志只是坏成了另一种样子。

写出前后各用一次随包发布的 assertReleasedV4Relationships():原件必须被判坏(并且是那句 developer/message does not match an open turn and step)、修好的必须能过,每一行还要单独过 assertV4RowAdmission();写完再读回来验证一次,解出来的字节必须还是同一份。--apply 会在同目录留一份 .jp-translate-corrupt.bak(读取器按文件名识别世代,多出来的 .bak 会被忽略,确认无误后可自行删除)。

tools/dsh-env/run-repair.mjs # 造一份坏日志 → 检测 → 修 → 读取器放行 → 幂等

设置页(浏览器半)的写法约束

设置页注册进 settings.section(root scope list),把控制器交给组件:

ctx.slots.inject('settings.section', () => ctx.slots.register({
name: 'settings.section', id: 'jp-translate', order: 60,
label: () => '日语翻译',
inject: () => controller.face(), // { controller, onSave, … }
}, GlossaryCard))

两条硬性经验,都是实际踩过的:

1. 组件自己订阅控制器,不要依赖渲染器绑定的 hook prop。
组件从 props.controller 拿 getSnapshot()/subscribe(),用
React.useState(read) + React.useEffect(...) 订阅——这是同仓可用的
@linxin666/dsh-session-archive 设置页的写法。用 inject 的 hooks 面再指望它变成 props.useXxx 时,组件会在 render 阶段抛错,渲染器的 SlotErrorBoundary 会把这个 entry 永久退役:Slot.inspect 里显示 active: false,设置面板只剩空白。
2. 任何一步都不许抛。
bindForm() 里每个可能失败的动作都走 safe() 并以 pending 快照兜底;getSnapshot()
永不抛,读失败就降级成带 error 字段的快照,页面把它显示出来。原因很直接:
bindForm() 在 apply() 里抛错会让整个设置页不被注册(不是空白,是根本没有);
而组件抛错会让 entry 被退役(有导航项,点开空白)。
run-client.mjs 用「永远抛错的表单」和「没有 controller」两种输入来钉住这两条。
3. 保存必须回读校验,不能凭 set() 的返回值宣布成功。
写入结果有两种“假成功”:
- 被拒绝:mutate 返回 false,随后异步用 Host 的旧值恢复(recover() →
mirror.load());
- 被接受但没落库:返回 true,而 Host 里仍是旧值。

两者只要被当成成功,卡片就会把草稿当成已保存、清掉 dirty 标记,紧接着表单推送旧值 → sync() 覆盖草稿 → 下拉框弹回「默认」、新建的预设消失(这就是本页报过的现象)。
所以 save() 在写入后用 storedMatches(doc) 把 Host 实际持有的文档读回来比对:只有内容一致才算保存成功;否则保留草稿(用户输入不丢)并显示「保存未生效…可重试」。
run-client.mjs 用两个 double 分别钉住这两条:refuseWrites()(拒绝 + 异步恢复旧值)与 dropWrites()(接受但不落库)。
4. 共享表单快照的 value 是「整个 namespace section」,不是本插件的那个字段。
configForms.get(entryId) 构造的控制器只带 { namespace: entryId }、没有 decode,
于是 ConfigFormController.decode() 原样返回 view.value;而 Host 那边是
projectForm(form, plainConfig(entry.fiber.config))——该 entry 所有 volatile 字段。
所以术语表读到的形状是 { glossary: '' },必须取 value.glossary
(client.js 的 fieldOf())再解析。

把整个 section 当文档解析的后果,正是本页报过的另一个现象:找不到 presets → 一律降级成默认文档 → 页面永远显示「默认」,且任何一次写入的回读都对不上,于是明明写进 profile patch 了,页面还是报「保存未生效」。
run-client.mjs 的 fakeForm() 按真实传输形状包一层 section 来钉住它(「the section object is decoded」用例:section 里放一个默认文档没有的 Pixiv 预设,字段/section 搞混就必然失败)。

改完源码后必须同步到已安装副本

plugin_manager(install_bundle, "file:") 是拷贝安装(pnpm),profile 里那份
不是软链——改完源码后要执行:

pwsh -File .\dsh-jp-translate\sync-installed.ps1
可选:-ProfileRoot

脚本先比对哈希(内容相同就跳过,因为运行中的 host 会长时间占用 package.json),再用 .NET File.Copy + 重试写入,最后按哈希复核。不要用 Copy-Item -Force:宿主占用或重载时它会报「cannot overwrite … with itself」或静默跳过,看起来像成功。

验证

tools/dsh-env/ 里放了一份可解析 @deepseek-ai/ 的 node_modules(从 app.asar 解出),用于在本机跑真实 Session 的行为验证:

tools/dsh-env/run-spec.mjs # 提示词 + 无历史不变式 + 「这份日志能加载吗」(真实 Session + 真实读取器) tools/dsh-env/run-host.mjs # host 半加载、预设声明、设置写入后重声明 tools/dsh-env/run-preset.mjs # 预设内模块:真实 SystemPrompt 装配、complete 段、pre-step 不写盘、request 遮蔽 tools/dsh-env/run-client.mjs # 浏览器 bundle:设置页注册、两个文本框、预设增删改 tools/dsh-env/run-notools.mjs # 真实汇总:装配出的 tools 必为空,且只对本预设的 agent 生效 tools/dsh-env/run-repair.mjs # 已损坏日志的修复工具:检测、重写、读取器复核、拒绝越权修改 tools/dsh-env/verify-installed.mjs # 已安装副本 9 个文件逐字节哈希一致 + 修复标记在位

所有 spec 都用直接路径导入本包(../../dsh-jp-translate/…),run-host.mjs 还断言 tools/dsh-env/node_modules/@workspace/dsh-jp-translate 不存在:那里曾经放过一份拷贝用来让裸包名解析,它悄悄变旧、spec 于是测的是旧版本——和手写替身同一类谎言。

run-spec.mjs 的关键断言:三轮对话后 session.deriveMessages() 恒为 [system, user],且 user 就是最新一期的原文;三轮写完的日志仍能被
assertReleasedV4Relationships() 加载,而把遮蔽放回 agent/pre-step 的位置就会被它
以线上那句原文拒绝。

run-preset.mjs / run-notools.mjs 用的是真实服务(SystemPrompt、ToolRuntime、 dsh-scope 作用域链),不是手写替身。教训写在这里:本包先后有三次「测试全绿但线上仍坏」,根因都是替身(或复刻的规则)比真实实现简单一级——设置页替身把 value 当成字段而不是整个 section,装配替身把作用域上下文当成作用域键,无历史替身把 step/start 摆在了遮蔽之前。凡是能用真实实现的,就不要写替身。
预览分享卡片长什么样 ↓wangjiezhe/dsh-jp-translate
分享 · DeepSeek Harness Hub
wangjiezhe/dsh-jp-translate

只做日译中,不带工具与历史消息

回到顶部