DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

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

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

Ets Hook Dsh Normalize Invisible

不可见钩子:一个 DeepSeek Harness 插件,挂接 llm/stream 流水线——每一次流式模型调用都可拦截的包装器——并规范化零宽度/不可见字符系列(U+00AD 软连字符、U+200B–U+200F 零宽度空格

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

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

兼容性与来源证明

Ets Hook Dsh Normalize Invisible 以 @playform/ets-hook-dsh-normalize-invisible 发布,当前版本为 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
未声明可安全裁剪
解包体积
40.4 kB
文件数
24
Surface
any
许可证
CC0-1.0
发布源
npm
GitHub
★ 1
周下载
419
安全扫描
✓ v0.0.1 扫描通过
最近提交
2026/10/17
查看源码 ↗项目主页 ↗
README Badge

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Codex Subscriptiondsh-codex-subscription通过 OAuth 在 DeepSeek Harness 中使用 ChatGPT 和 Codex 订阅,支持配额管理、安全重置、网页搜索、图像和快速模式Commandcode Provider@mars-sea/dsh-commandcode-provider适用于 Command Code 的非官方 DeepSeek Harness LLM 提供商插件,移植自 pi-commandcode-provider(MIT)。注册“commandcode”提供商路由,并在 Models 页面添加卡片和实时模型目录。Connect Workbuddydsh-connect-workbuddy把本机登录的 WorkBuddy 模型接入 DeepSeek Harness,并提供只读的积分概览与模型管理。Damage Pulsedsh-damage-pulseDeepSeek Harness 余额监控器,配有鲸鱼女孩伙伴,并为每次令牌消耗提供感知缓存的伤害动画。

README

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

The DeepSeek Harness Plugin Family for PlayForm.

[!NOTE]

The invisible-character 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 zero-width/invisible character family in model output, live in the transcript - by REMOVING it: zero-width spaces, joiners, bidi controls and the BOM character simply vanish (the default replacement is the empty string).

A CLASS flavor of the normalize family: the core's Invisible class plus a configurable replacement string (default "" - removal).

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

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 @ Invisible): a hook child of the ets-plugin-dsh-factory service and the ets-hook-dsh-core machinery; the fifth 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 Invisible class and the generic Replace/Chunk/Block dispatch:

flavortablesubstitution
ets-hook-dsh-normalize-dashcore Dashes class→ replacement (default -)
ets-hook-dsh-normalize-quotescore Quotes MAPcurly → straight
ets-hook-dsh-normalize-ellipsiscore Ellipsis classU+2026 → ...
ets-hook-dsh-normalize-spacescore Spaces classunicode spaces → " "
ets-hook-dsh-normalize-invisible (this bundle)core 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-invisible
  1. Remote / dependency install (auto-activation): pnpm add @playform/ets-hook-dsh-normalize-invisible 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 (this bundle's own pnpm-workspace.yaml - packages: [.], nodeLinker: hoisted - keeps the install local) → npm run prepublishOnly to build Target/ → dsh plugin --profile <name> add <this directory> and a restart.

Activation is at boot; verify with the ets-hook-dsh-normalize-invisible: activated (...) ledger line (the --dump-config verification path is gone behind the app-managed profile guard - the ledger and the runtime's inspect providers are the activation assessors).

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-invisible
        name: "@playform/ets-hook-dsh-normalize-invisible"
        config:
            log: true
            logFile: ~/.dsh/ets-hook-dsh-normalize-invisible.log
            replacement: ""

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

Zero-width and format characters carry no visible width - which makes them the perfect smugglers.

A zero-width joiner inside a file path, a right-to-left mark inside a command, the U+202E visual-spoofing override in what looks like plain text: none of them show up in the transcript, all of them change what a parser, shell or diff sees.

This flavor deletes the whole family before it 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 ────────► Replace(text, Invisible, "")  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 ───► Replace(argumentsDelta, Invisible, "") 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-invisible: normalized N invisible char(s) in one stream`

The transform

The core's Invisible class (see ets-hook-dsh-core), applied per text segment through the core's generic class-to-string Replace:

[\u00AD\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2060\uFEFF]  →  replacement
U+ codepointsCharacters
00ADsoft hyphen
200Bzero-width space
200Czero-width non-joiner
200Dzero-width joiner
200Eleft-to-right mark
200Fright-to-left mark
202A-202ELRE, RLE, PDF, LRO, RLO (bidi embedding controls; U+202E is the classic visual-spoofing override)
2060word joiner
FEFFzero-width no-break space (the BOM character)

All are removed (default replacement: "" - REMOVAL) per text segment:

  • No context rules, chunk-boundary-safe - single-character replacement, no lookahead, per-chunk application can never disagree with whole-text application.
  • The replacement is applied with a function replacer, so a custom replacement containing $ patterns is inserted literally.
  • Replaced characters are counted per stream for the ledger line.

The Pitfalls

  • Removal is the replacement - replacement: "" (the default) removes the family (soft hyphen, zero-width spaces and joiners, bidi controls, word joiner, BOM); a hot edit to a non-empty replacement turns the pass into a replace instead.
  • 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 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-invisible.logyesthe invisible ledger (separate from the family's logs)
replacementstring""yesthe transform's only knob - default REMOVAL; hot-editable
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

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


In Action

One stream, one deletion.

The model emits text carrying invisible smugglers; the live UI and the transcript receive clean text - and the two lines below look identical in print, because the removed characters are invisible.

The incoming line carries, in order: a soft hyphen (U+00AD), a zero-width space (U+200B), a zero-width joiner (U+200D), a left-to-right mark (U+200E), a right-to-left mark (U+200F), a word joiner (U+2060) and a BOM character (U+FEFF):

text-delta in (what the model wrote):

  pass­s​word‍:‎ correct‏⁠

text-delta out (what reaches the transcript):

  "password: correct"

In the incoming line the smugglers sit inside the word password and around the colon; a diff would see a password that does not match the author's text, a parser would tokenize differently, a shell could splice a command.

After the pass all of them are simply gone - the output line is what the reader should see.

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

ets-hook-dsh-normalize-invisible: normalized 7 invisible char(s) in one stream

The replacement is hot-editable (the default "" removes; a non-empty value such as "?" would mark each smuggler's position for debugging), and 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 raw-marker calls passing through unnormalized.


The Ledger

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

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

The empty default replacement renders as the empty replacement=.

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.