DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Hook Dsh Normalize Fullwidth — DSH Plugin for DeepSeek Harness
← Plugins

@playform/hook-dsh-normalize-fullwidth

Hook Dsh Normalize Fullwidth

The fullwidth hook: a DeepSeek Harness plugin that hooks the llm/stream waterfall — the interceptable wrapper around EVERY streaming model call — and normalizes the full-width family (the entire FULLWIDTH FORMS range U+FF01–U+FF5E) in model output streams

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add @playform/hook-dsh-normalize-fullwidth@0.0.1
READMECompatibilityVersions

Compatibility and provenance

Hook Dsh Normalize Fullwidth is published as @playform/hook-dsh-normalize-fullwidth and currently resolves to version 0.0.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
npm
Registry updated
10/16/2026

Versions

0.0.1stable
10/9/2026

Related plugins

Loading related plugins…

Latest
0.0.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
38.8 kB
Files
24
Surface
any
License
CC0-1.0
Source
npm
GitHub
★ 1
Weekly downloads
0
Last push
10/16/2026
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in models-usage.

Whale Widgetdsh-whale-widgetDeepSeek balance whale widget in the bottom-right corner of the DSH Web interface: balance/today’s usage/peak-off-peak pricing, customizable bubble click sequence (text/balance/today/peak-off-peak/image/random phrases and parallel weighted selection), per-line styles and fonts, floating quick editinUsage@linxin666/dsh-usageUsage statistics plugin for the dsh web GUI: per-provider balance and coding-plan quota detection plus a live token usage ledger, with a compact price glance card above the sidebar's Settings rowClient Ui Model Capabilities@linxin666/dsh-client-ui-model-capabilitiesPer-model capability declarations (image input and reasoning efforts) for custom pi-ai providers, edited in place on the Models settings cards; writes the official llm-pi-ai settings namespace, no DSH source changes.Cost Meterdsh-cost-meterDeepSeek Harness session cost statistics plugin: current session cost, today’s charges, historical records, and synchronization with official pricing; supports multi-provider, multi-model price calculation (built-in pricing catalog for 170+ model IDs with automatic matching); queries and displays su

README

@playform/hook-dsh-normalize-fullwidth

The DeepSeek Harness Plugin Family for PlayForm.

[!NOTE]

The fullwidth 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 full-width family in model output, live in the transcript: the entire FULLWIDTH FORMS range U+FF01-U+FF5E maps to its ASCII half-width counterpart U+0021-U+007E - the whole story, letters, punctuation and digits included.

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

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

The family's raw-write tool (registered by 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 @ Fullwidth): a hook child of the plugin-dsh-factory service and the hook-dsh-core machinery; the sixth of the six stream normalizer siblings.

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

flavortablesubstitution
hook-dsh-normalize-dashcore Dashes class→ replacement (default -)
hook-dsh-normalize-quotescore Quotes MAPcurly → straight
hook-dsh-normalize-ellipsiscore Ellipsis classU+2026 → ...
hook-dsh-normalize-spacescore Spaces classunicode spaces → " "
hook-dsh-normalize-invisiblecore Invisible classremoved (default "")
hook-dsh-normalize-fullwidth (this bundle)core 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/hook-dsh-normalize-fullwidth
  1. Remote / dependency install (auto-activation): pnpm add @playform/hook-dsh-normalize-fullwidth 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 hook-dsh-normalize-fullwidth: 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: hook-dsh-normalize-fullwidth
        name: "@playform/hook-dsh-normalize-fullwidth"
        config:
            log: true
            logFile: ~/.dsh/hook-dsh-normalize-fullwidth.log
            normalizeReasoning: true

The Problem

CJK input methods emit full-width punctuation, digits and letters - and a full-width comma or digit in a command, code block or path is invisible on screen and wrong for every parser, shell and diff.

This flavor maps the whole fullwidth range to the unambiguous ASCII half-width forms 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 ────────► ReplaceMap(text, Fullwidth)  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, Fullwidth) 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 ──► `hook-dsh-normalize-fullwidth: normalized N fullwidth char(s) in one stream`

The transform

Exactly the core's Fullwidth map (@playform/hook-dsh-core's Normalize/Fullwidth), applied per text segment through the core's ReplaceMap - the standard fullwidth-to-halfwidth mapping over the whole range, described by code point here (the literal characters are in the In Action block below):

Code pointContentsASCII
U+FF01-U+FF0Ffull-width punctuation (exclamation through solidus)!"#$%&'()*+,-./
U+FF10-U+FF19full-width digits zero through nine0123456789
U+FF1A-U+FF20full-width punctuation (colon through commercial at):;<=>?@
U+FF21-U+FF3Afull-width capitals A through ZA-Z
U+FF3B-U+FF40full-width punctuation (left square bracket through grave)[\]^_``
U+FF41-U+FF5Afull-width small letters a through za-z
U+FF5B-U+FF5Efull-width punctuation (left curly brace through tilde){|}~
  • No context rules - one character in, its half-width 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 - the table's backslash, circumflex, vertical bar and tilde values are exactly the characters a string replacement would corrupt).
  • Replaced characters are counted per stream for the ledger line.

The Pitfalls

  • The range is exact - only U+FF01-U+FF5E maps to its ASCII half-width counterpart; the fullwidth space U+3000 is the spaces flavor's character, not this table's.
  • 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/hook-dsh-normalize-fullwidth.logyesthe fullwidth 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 CJK-typing artifacts - a full-width word, a full-width path punctuation run, a full-width digit; the live UI and the transcript receive the half-width ASCII forms:

text-delta in (what the model wrote):

  path "/usr/local/bin" — version 123 — done!

text-delta out (what reaches the transcript):

  path "/usr/local/bin" — version 123 — done!

Every full-width code point in the incoming text - the solidus U+FF0F, the letters U+FF55-U+FF4E, the digits U+FF11-U+FF13 and the exclamation U+FF01 - becomes its U+0021-U+007E half-width counterpart; ASCII characters that were already half-width are untouched.

This is what keeps a pasted path openable and a pasted number parseable.

The backslash, circumflex, vertical bar and tilde entries are exactly the characters a naive string replacement would corrupt - hence the function replacer.

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

hook-dsh-normalize-fullwidth: normalized 4 fullwidth 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 raw-marker calls passing through unnormalized.)


The Ledger

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

hook-dsh-normalize-fullwidth: activated (reasoning=on, toolArgs=off, logFile=~/.dsh/hook-dsh-normalize-fullwidth.log)
hook-dsh-normalize-fullwidth: normalized N fullwidth 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.


The two release groups (CLASSIC vs EFFECT-TS)

Two published groups, one contract: this package is the CLASSIC build - plain TypeScript, zero framework dependencies: import ... from "@playform/hook-dsh-normalize-fullwidth" resolves to Target/. The EFFECT-TS implementation of the same contract is the separate package @playform/ets-hook-dsh-normalize-fullwidth - effect-backed, built on the shared @playform/ets-base-dsh base with effect v4.0.2 as its runtime dependency. Install the group you run and never mix the groups in one graph (the effect-ts Update returns Effect envelopes, not the classic dispatch envelope).

License 📜

CC0-1.0.