DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Ets Hook Dsh Normalize Quotes — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

@playform/ets-hook-dsh-normalize-quotes

Ets Hook Dsh Normalize Quotes

quotes hook:一个 DeepSeek Harness 插件,它挂接到 llm/stream waterfall 上——这是每一次流式模型调用的可拦截包装器——并规范化弯引号系列(U+2018 左单引号、U+2019 右单引号以及 U+

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add @playform/ets-hook-dsh-normalize-quotes@0.0.1
README兼容性版本

兼容性与来源证明

Ets Hook Dsh Normalize Quotes 以 @playform/ets-hook-dsh-normalize-quotes 发布,当前版本为 0.0.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
npm
Registry 更新时间
2026/10/17

版本

0.0.1stable
2026/10/9

相关插件

正在加载相关插件…

最新版
0.0.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
38.5 kB
文件数
24
Surface
any
许可证
CC0-1.0
发布源
npm
GitHub
★ 1
周下载
393
安全扫描
✓ v0.0.1 扫描通过
最近提交
2026/10/17
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Experimental Inspector@deepseek-ai/dsh-experimental-inspector用于 Host 调试和 Client Runtime 检查的实验性跨 realm CDP hub

README

@playform/ets-hook-dsh-normalize-quotes

The DeepSeek Harness Plugin Family for PlayForm.

[!NOTE]

The quote normalizer for model output - a DeepSeek Harness plugin that hooks the llm/stream waterfall (the interceptable wrapper around EVERY streaming model call, bound to the LlmRuntime) and normalizes the curly quote family in model output, live in the transcript: the eight typographic quote code points each map to their ASCII straight counterpart - U+2018/U+2019/U+201A/U+201B to the ASCII apostrophe, and U+201C/U+201D/U+201E/U+201F to the ASCII double quote.

A MAP flavor of the normalize family: the core's Quotes char-to-char table owns the substitution - no replacement config knob.

The @-sentence identity: Hook @ DSH @ Normalize @ Quotes.

The family's raw-write tool (registered by ets-hook-dsh-normalize-dash) bypasses this flavor's transforms too - the exemption is family-wide: the core's Stream/Chunk + Stream/Block pass every raw-write call through by identity, so the tool's explicit normalize parameter (default false = verbatim) is the only normalization it applies.


Where It Fits

Family position (the @-sentence Hook @ DSH @ Normalize @ Quotes): a hook child of the ets-plugin-dsh-factory service and the ets-hook-dsh-core machinery; the second of the six stream normalizer siblings.

One of six siblings in the stream-normalization family, all built on the shared machinery - ets-plugin-dsh-factory for State/Append (and the named Schema helper, shared: false, for the config), ets-hook-dsh-core for the Quotes table and the generic ReplaceMap/Chunk/Block dispatch:

flavortablesubstitution
ets-hook-dsh-normalize-dashcore Dashes class→ replacement (default -)
ets-hook-dsh-normalize-quotes (this bundle)core Quotes MAPcurly → straight
ets-hook-dsh-normalize-ellipsiscore Ellipsis classU+2026 → ...
ets-hook-dsh-normalize-spacescore Spaces classunicode spaces → " "
ets-hook-dsh-normalize-invisiblecore Invisible classremoved (default "")
ets-hook-dsh-normalize-fullwidthcore Fullwidth MAPfull-width → half-width

A non-manifest factory consumer: it injects ["pluginFactory"] and uses State (cell unwrap + shared Ledger/Enabled mappings + its own fields), Append and Journal (the shared P5 record on every N > 0 pass); the config is composed by the factory's standalone Schema helper with shared: false - the minimal block, no fs/observed dead fields.

It touches no files, so fs/write-intent and fs/observed never see it; it wraps the downstream result and always calls next(), so it composes with other llm/stream listeners regardless of registration order.

The DSH plugin family is the DeepSeek Harness plugin layer of the PlayForm ecosystem: TypeScript-first Source/ → Target/, the deterministic @playform build, prepublishOnly-only - the same conventions as every other @playform package.

Install

Terminal (registry)

pnpm add @playform/ets-hook-dsh-normalize-quotes
  1. Remote / dependency install (auto-activation): pnpm add @playform/ets-hook-dsh-normalize-quotes in a profile dir (+ the package name in dsh.profile.bundles, or dsh plugin add) - installed automatically and activated at the next host start.
  2. Local git clone: clone → pnpm install inside the bundle (its pnpm-workspace.yaml pins packages: [.] so the install lands in the bundle, not the workspace root) → build → dsh plugin --profile <name> add <this directory>.
  3. Tarball / npm: dsh plugin --profile <name> add <tarball-or-spec> - the package ships built Target/ (minified, .d.ts twins), so no build runs at install time.

Verify with the ets-hook-dsh-normalize-quotes: activated (...) ledger line.

Usage

The bundle is configured through its cordis.patch.yml row (or the profile's dsh.bundle manifest) - the full config table is in The Config:

- insert:
      - id: ets-hook-dsh-normalize-quotes
        name: "@playform/ets-hook-dsh-normalize-quotes"
        config:
            log: true
            logFile: ~/.dsh/ets-hook-dsh-normalize-quotes.log
            normalizeReasoning: true

The shared Effect-TS plumbing - the runtime wiring, the stream machinery and the hook contract types - comes from the base ets-base-dsh package.


The Problem

Smart-quote processors and code editors emit typographic quotes; every parser, shell and diff understands the straight ASCII ones.

Left in model output, a curly quote is a silent correctness hazard - in code blocks, commands and file paths it is simply the wrong character.

This flavor makes the straight form the one that reaches the transcript.


How It Works

The stream pipeline

  llm/stream waterfall (options, next)     the interceptable wrapper around
       │                                   EVERY streaming model call
       ▼  next() called FIRST, always - options never touched
  Normalize(upstream, state)               the async generator
       │   for await (chunk of upstream)
       ▼
  CoreChunk(chunk, transform, reasoning, toolArgs, raw)  the core's per-chunk dispatch
       │
       ├─ text-delta ────────► ReplaceMap(text, Quotes)     rewrite the text field
       ├─ reasoning-delta ───► same, when normalizeReasoning (default ON)
       ├─ block-end ─────────► the assembled block's text fields -
       │                       TextBlock.text / ReasoningBlock.text (plus a
       │                       runtime `thinking` string field) - the deltas
       │                       AND the block must agree, or consumers see
       │                       inconsistencies
       ├─ tool-call-delta ───► ReplaceMap(argumentsDelta, Quotes) when
       │                       normalizeToolArguments (IMPLEMENTED, default
       │                       OFF - execution-critical raw JSON, the user's
       │                       accepted risk; the example patch turns it
       │                       on) - the assembled ToolCallBlock.arguments
       │                       follows the same flag via block-end
       │                       The gate is THREE-WAY with the flag on: a
       │                       delta/block whose `name` is "edit", "raw-write"
       │                       or "normalize-file" passes through BY IDENTITY
       │                       (the edit tool's `old_string` must match the real
       │                       file bytes; the raw-write tool owns its content
       │                       normalization; the normalize-file tool's arguments
       │                       carry a file path - a normalized dash inside a
       │                       filename would corrupt the target),
       │                       and a call whose arguments open with the
       │                       `{"__normalize":false` marker (FIRST key,
       │                       tracked per call id) passes through
       │                       UNNORMALIZED with the marker entry stripped,
       │                       so the executed call carries no unknown key
       └─ block-start / usage / finish
                                ──► PASSTHROUGH BY IDENTITY, ALWAYS
                                    (usage/finish ordering is the
                                     adapter contract)
       │   count === 0 → original chunk BY IDENTITY; rewritten → shallow copy
       ▼
  yield ──► downstream consumers = the live UI + the durable transcript
       │    (order preserved, no buffering; upstream throws propagate)
       ▼  normal loop completion, Count > 0
  Factory.Append ──► `ets-hook-dsh-normalize-quotes: normalized N quote char(s) in one stream`

The transform

Exactly the core's Quotes map (@playform/ets-hook-dsh-core's Normalize/Quotes), applied per text segment through the core's ReplaceMap - eight entries, each typographic quote code point to its ASCII straight counterpart:

Code pointCharacter (by name)ASCII
U+2018left single quotation mark' (U+0027)
U+2019right single quotation mark'
U+201Asingle low-9 quotation mark'
U+201Bsingle high-reversed-9 quotation mark'
U+201Cleft double quotation mark" (U+0022)
U+201Dright double quotation mark"
U+201Edouble low-9 quotation mark"
U+201Fdouble high-reversed-9 quotation mark"
  • No context rules - one character in, its straight counterpart out.
  • Chunk-boundary-safe: single-character substitution, no lookahead - per-chunk application can never disagree with whole-text application.
  • The mapping values are returned through a function replacer, so they are inserted literally (no $-pattern interpretation).
  • Replaced characters are counted per stream for the ledger line.

The Pitfalls

  • A dependency alone is NOT activation - the package must be in dsh.profile.bundles (or the patch row inserted), or no listener registers and no ledger exists.
  • The map is quote-only - the em dash U+2014 and the rest of the dash family are the dashes flavor's class; a quotes-only pass leaves them untouched - run the flavors the session's characters call for.
  • The stream gate rewrites model output only - it never touches disk; files already on disk are the normalize-file tool's job.
  • The count is per replaced code point - N counts matched characters; N = 0 and an upstream throw both write no count line and no journal record.

The Config

FieldTypeDefaultVolatileMeaning
logbooleantrueyeswrite the durable ledger file
logFilestring~/.dsh/ets-hook-dsh-normalize-quotes.logyesthe quotes ledger (separate from the family's logs)
normalizeReasoningbooleantruenonormalize reasoning deltas and the assembled reasoning block too
normalizeToolArgumentsbooleanfalsenoIMPLEMENTED (default OFF): rewrite the tool-call argumentsDelta and the assembled ToolCallBlock.arguments when on (with three exemptions: edit, raw-write and normalize-file calls pass through by identity (the edit tool's old_string must match the real file bytes; the raw-write tool owns its content normalization through its explicit normalize parameter; the normalize-file tool's arguments carry a file path - a normalized dash inside a filename would corrupt the target); and a call whose arguments open with the {"__normalize":false first-key marker passes through unnormalized, the marker entry stripped) - execution-critical raw JSON, the user's accepted risk; the example patch turns it on

There is no replacement field (MAP flavor).

Volatile cells commit without remounting the plugin; the factory's State builder unwraps them defensively.


In Action

One stream, one transformation.

The model emits smart-quoted prose; the live UI and the transcript receive the straight ASCII forms:

text-delta in (what the model wrote):

  ‘She said “let’s ship it” — today’s the day’

text-delta out (what reaches the transcript):

  'She said "let's ship it" — today's the day'

Every typographic quote in the incoming text - the four single forms and the four double forms - becomes its ASCII counterpart; straight quotes that were already there are untouched (the count only counts replacements). The sample carries six of the eight forms; the em dash U+2014 is the dashes flavor's character, not the quotes map's - with only the quotes flavor it passes through untouched.

In code blocks, commands and file paths this is the difference between a command that runs and one that fails for no visible reason.

When a stream finishes normally with replacements made, the ledger gets the count line:

ets-hook-dsh-normalize-quotes: normalized 6 quote char(s) in one stream

(The same pass runs over reasoning deltas when normalizeReasoning is on, and over tool-call arguments when the example patch enables normalizeToolArguments - with the edit, raw-write and normalize-file names exempt and the raw-marker calls passing through unnormalized.)


The Ledger

Two lines, both written through the factory's Append (the ets-hook-dsh-normalize-quotes: prefix is the logger's <State.Module>:; the durable file line is [<ISO>] <message>):

ets-hook-dsh-normalize-quotes: activated (reasoning=on, toolArgs=off, logFile=~/.dsh/ets-hook-dsh-normalize-quotes.log)
ets-hook-dsh-normalize-quotes: normalized N quote char(s) in one stream

The activation line is written by apply(); the count line only follows a normal stream completion and only when N > 0 (a thrown-away stream writes no ledger line).

Every N > 0 pass also journals one normalized record into the shared package_governance v2 domain - event normalized, path = the empty stream-level path, detail byte-identical to the count line - best-effort: with no storage facility the record buffers or drops, and the human ledger stays the complete record.


License 📜

CC0-1.0.