Novel-writing guardrails: 20 novel_* tools enforce fact-ledger consistency, phase and chapter gates, machine audit with deterministic metrics, and proposal-only revisions; a right-panel workbench covers reading, TTS playback, polish, proofread, continuity checks and batch drafting, and a bypass engine runs internal steps off the main conversation.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-novel-forge
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:huangziyuan-general/dsh-novel-forge
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
English | 中文文档
A novel-writing plugin for DeepSeek Harness (DSH). It has exactly one design spine:
Code enforcement beats prompt discipline. Any constraint that relies on the model "behaving itself" will eventually fail — consistency, gating, and auditing must be hard constraints at the tool/data layer.
Why this isn't another "prompt collection"
Most writing plugins put their constraints in system prompts — the model obeys when it feels like it and routes around them under deadline pressure. This plugin puts the constraints in code. Three verifiable facts (each checkable against the source in this repo):
- One judgment chain behind all three entrances: the panel "💾 Save",
novel_write_chapter, and REST writes all run the same gating code (mechanical check / ledger conflicts / content gate / outline contract) — no bypass; the single override switch is explicitly audited. - The model cannot self-approve proposals: for revisions, the model can only register a proposal, never apply it — "Apply" is the user's sovereign action in the panel (there is no apply tool on the model side); outline approvals record their channel, and the dashboard tracks the "tool self-approval ratio" (real-machine evidence: a serialized book once hit 168/168 self-approvals — the first gap that let its outline drift).
- Zero-token auditing: mechanical checks, the AI-smell scan, platform reviews, consistency checks, and point-in-time ledger simulation are all pure local computation — per-chapter audit cost is 0, and the numbers never pass through the model's hands.
UI Tour
The right-sidebar "Forge" panel, rendered from the real lib/client.js (these are fictional sample data, not real books):
| Project list | Detail · Overview (full-book health dashboard) |
| Polish guard rejects (core selling point: code says no, with full reasons) | Batch drafting (concurrent gen · serial gating · checkpoint resume) |
| Chapter text-to-speech | Worldbook |
The screenshots above are real renders of
lib/client.js(not hand-drawn mocks): runnpm run previewto generate the preview page, thenassets/mockups/shoot-real.cjsto reproduce — both light and dark themes track the host.
The failure modes of AI long-form writing aren't magic — each has an engineering answer. This plugin encodes the parts with a clear right/wrong into code:
| AI writing failure mode | This plugin's hard constraint | Entry point |
|---|---|---|
| Setting / power-scaling collapse | Fact ledger: same-chapter value change = conflict rejection; worldbook keywords auto-injected | novel_ledger / novel_worldbook |
| Model "can't see prior text" | Pre-write briefing assembles a context pack within budget (outline → character cards → ledger → foreshadowing → worldbook → previous chapter ending) | novel_briefing |
| Skipping stages verbally | Stage gate: if the chapter outline isn't approved, novel_write_chapter rejects outright |
novel_outline approve |
| Cross-chapter repetition / padding | 8-character shingle overlap detection against prior text; beyond threshold, save is rejected | novel_write_chapter mechanical check |
| Uncontrolled chapter length | Mechanical-check threshold defaults to 2000–4000 chars (serial-web novel standard, target 3000); the pre-write briefing carries a "target chapter length" section | novel_write_chapter / novel_briefing |
| No hook at chapter end | Chapter-end hook heuristic (question / suspense / ellipsis / exclamation); missing one raises a warning | novel_audit |
| AI smell | Eight-dimension structural scan: template sentences / stock-phrase density / emotional directness / sentence-pattern templates / paragraph-rhythm variance / information dilution / chapter-end formula / AI default words (fully local, zero model cost) | novel_noai_scan |
| Silent overwriting of text | Proposal system: edits go through a proposal → user confirms → new version is generated; old versions are never overwritten | novel_propose |
| Lax reviewing | Mechanical and model review are separated: the audit produces deterministic numbers, and model review must cite evidence | novel_audit |
| Spoiling the suspense early | Scene contract: hidden-character dossiers never enter context (traces erased even from ledger/summaries); if the name appears in the body, the content gate rejects it | novel_scene |
| Drifting from / missing the outline | Outline contract sections (must-write scenes / forbidden deviations) are scored by code for coverage and deviation; hitting a forbidden item rejects the commit | novel_write_chapter |
| Every character sounds the same | Voice-gene cards are injected structurally (sentence length / logic / catchphrases / never-says / small gestures / register); review checks against taboo words | novel_character voice |
| Context explosion in long works | When a contract is present, only cards of characters appearing in this chapter are injected; worldbook is fetched by whitelist | novel_briefing |
| Wanting to outline before settings are set | Nine-stage state machine: each stage's entry condition is judged by code, and what's missing is listed directly | novel_project phase |
| Chapter one comes out too short | The outline is scored on scene count + beats (calibrated on 151 real chapters); if it can't reach the floor it is rejected, naming which scene has only one beat; the pre-write briefing then gives a per-scene word budget | |
| Editing in the panel bypasses the gates | The panel's save button shares the same judgment chain as chapter writing (only the outline stage gate is waived): short drafts, placeholders, resurrected characters and spoilers return 409 with the original text, with one explicit "save anyway (audited)" when needed | panel editor |
| Same chapter keeps going wrong | Circuit breaker: 3 consecutive rejections on the same chapter triggers a write-block (not a reminder); cleared automatically once the outline/contract is fixed | novel_write_chapter |
| Doesn't fit platform taste | Five platform review tables: Qidian checks structure / chapter-end hook; Fanqie checks the first 1000 chars for payoffs; Jinjiang checks chapter-length floor and a no-suffering opening; Qimao checks strong emotion and the chapter-end hook; Feilu checks ultra-fast pacing / an early golden finger / payoff density | novel_audit platform |
| Crossing platform red lines | Seven-category sensitivity self-check; hits return line numbers and fixes | novel_audit censor |
| Internal chores flood the main chat | Side-channel direct calls: polish / proofread / annotate / draft run on independent flows — no main-chat occupation, no session-log writing; results surface only as proposals | lib/engine.js |
| Batch drafting bypassing gates | Concurrent generation, serial commit: every chapter still passes the same gate; a single-chapter failure doesn't roll back the whole batch | draft-batch |
| Batch drafting interrupted midway | Checkpoint: after each chapter commits, .novel/batch-checkpoint.json is written; resume:true continues from the checkpoint — committed chapters are skipped automatically, only unfinished ones retry |
draft-batch resume |
| Same session keeps getting shorter | Session-fatigue detection: first drafts declining within the session (second half < 75% of the first half), or a session average under 70% of other sessions' first drafts, or ≥8 consecutive chapters in one session, triggers a warning; "create new session and hand off" builds a new session in one click (with archiving if available), and the handoff summary auto-copies to clipboard | Panel fatigue banner |
| "Can't see prior text" after 100 chapters | Local search index: retrieves paragraphs by memory fragments (manual Chinese bigram segmentation, zero dependencies) | novel_search |
Quick Start (~5 minutes)
Prerequisites: DeepSeek Harness (DSH) installed with a working model; Node ≥ 22 (22.19+ / 24+ enables the search index, otherwise it auto-degrades without affecting other features).
Step 1 · Install the plugin (pick one):
# from npm (published since 0.16.0; installs count toward dshmarket's "Most installed" board)
dsh plugin --profile web add npm:dsh-novel-forge
# from a public GitHub repo (⚠️ the github: shorthand goes through pnpm → SSH:
# the machine needs a GitHub SSH key plus github.com in ~/.ssh/known_hosts, otherwise
# updates fail with "Host key verification failed"; without SSH, use the npm source
# above — pulling a public package needs no credentials at all)
dsh plugin --profile web add github:huangziyuan-general/dsh-novel-forge
# local dev (symlink, edits take effect immediately)
dsh plugin --profile web add link:/path/to/dsh-novel-forge
Step 2 · Restart DSH web, then hard-refresh the browser (the client bundle is snapshotted at process start; without a restart it's as if you never installed it).
Step 3 · Verify the install: a standalone "🔨 Forge" tab appears in the right sidebar; in a session, ask the model to "list the novel tools" — you should see 20 novel_* tools. The agent preset "Novel Forge" is also auto-deployed to ~/.dsh/.agent-presets/novel-forge/ (skipped if already present, never overwritten; DSH_NOVEL_FORGE_REDEPLOY=1 forces a redeploy, DSH_NOVEL_FORGE_SKIP_DEPLOY=1 disables it).
Step 4 · Create your first book: just tell the model in a session, e.g.:
Use novel_project init to create a fantasy book titled "Example Title", with the one-line premise "a good-for-nothing awakens a power that was never meant to be his".
Step 5 · Follow the nine stages: the model guides you through premise → setting → characters → outline → volumes → chapter outlines → body, in order; at each stage it directly lists what's missing. You must novel_outline approve the chapter outline before writing it — that's by design, not a bug.
Step 6 · Collect results in the panel: in the right-sidebar "Forge" tab you can read / listen, click polish / proofread (outputs are proposals — you click "apply" in the panel to make them take effect), run the full-book health check, and export the whole book.
Host version requirements and the dependency surface are in COMPATIBILITY.md.
Division of labor: session vs. panel
- In a session (model +
novel_*tools): all creative actions — create book, outline, detailed outline, write chapter, ledger, audit. Writing a chapter must go through the session (briefing + multi-stage commit gates form a multi-turn loop). - Panel (right-sidebar "Forge"): read & listen, polish / proofread (side-channel engine, outputs proposals), full-book health check, golden-three-chapter diagnosis, batch drafting (with checkpoint resume), proposal approval (apply / discard), book-card management (rename / delete / clone), session-fatigue monitoring & one-click handoff.
Typical session flow:
novel_project init → novel_project phase (see the nine-stage board, fill entry conditions per the prompts)
→ novel_outline save_book → novel_character save (build a voice-gene card)
→ novel_worldbook add (lock in core settings) → novel_project phase stage:outline/volume (volumes)
→ loop:
novel_outline save_chapter + approve
→ novel_briefing (get the context pack; if a scene contract exists, trim by it)
→ draft per the outline → novel_write_chapter (gate / mechanical check / ledger / content gate / contract metrics / circuit breaker / commit)
→ novel_noai_scan + novel_audit (evidence) → you (or the model, citing evidence) review
→ before publishing: novel_audit platform:qidian|fanqie|jjwxc|qimao|feilu + novel_audit censor:true
→ revisions: novel_propose propose → user confirms in panel → v2 generated (old version never overwritten)
Panel feature overview
| Feature | Entry | Description |
|---|---|---|
| Project list | "🔨 Forge" tab | filtered by session (projects follow the session); rename / delete / clone; filter box appears above 8 books |
| Claim old books | bottom of list | books built before 0.5.0 with no owner, claim to current session in one click |
| 🎛 Creation cockpit | detail page | automatically aggregates progress, health checks, outline, proposals, diagnosis, and book-scoped char limits; samples the first 3 plus latest 12 chapters for platform review, so the next action comes before a pile of per-chapter tables |
| 📋 Overview | detail page | archive / outline / character cards / timeline summary; read chapter (📖 reading card), save, export, golden-three-chapter diagnosis |
| 🎧 Chapter TTS | detail page | TOC + continuous speech (pause / resume / stop, auto-advances to next chapter when done); each row has 📖 read / ▶ speak, reading and listening are independent |
| One-click polish / mechanical proofread | chapter | runs through the side-channel engine, output is a proposal, doesn't touch the body directly; proofread guard is stricter (length cap 1.06) |
| Full-book health check | detail page | dead-character revival / ledger contradictions / overdue foreshadowing / chapter-number gaps / missing character cards, zero token |
| Golden-three-chapter diagnosis | overview card | four-axis numbers (hook / opening / conflict / infodump) + overall verdict, pure wordlist, zero token |
| Batch drafting | detail page | concurrent generation, serial commit; per-chapter success/failure laid out; concurrency cap 4, default 1; after interruption "continue last batch (chapters a–b, N committed)" resumes from checkpoint |
| Session fatigue & handoff | detail-page banner | within-session first-draft decline / session average under 70% of other sessions' first drafts / ≥8 consecutive chapters triggers a warning (the write-progress card carries a session-status line, labelled "first draft = pre-gate draft"); "create new session and hand off" builds + archives a new session in one click, handoff summary auto-copies to clipboard, paste into the new session to continue seamlessly |
| Proposal approval | detail page | view full proposal → apply (generate new version) / discard; old version never overwritten |
23 tools
| Tool | Responsibility | Hard constraint |
|---|---|---|
novel_project |
init / status / phase (nine-stage board & entry judgment) / set_stage / repair / check / promise | prevents duplicate creation; stage entry judged by code; out-of-order recorded as skipped; repair reconciles index vs disk |
novel_outline |
full-book outline / chapter-N detailed outline / approve | the key to the write-chapter gate |
novel_character |
character cards + voice-gene cards (voice: sentence length / logic / catchphrases / never-says / small gestures / register, structured storage) | injected as a block when writing; audit voice:true checks taboo words |
novel_worldbook |
worldbook entries (add/update/list/remove + import/export) | settings only recognized here; update preserves id; a missing trigger no longer errors — known entity names are derived from the content, otherwise the entry becomes always-on with a visible warning |
novel_scene |
scene contract (save/get/list/delete): this-chapter scenes / cast / hidden characters / worldbook whitelist / forbidden items | hidden-character dossiers never enter context and their names are forbidden in the body; off-contract characters aren't injected (saves tokens) |
novel_briefing |
pre-write context pack (outline → pledge → scene contract → character cards → voice-gene cards → ledger → foreshadowing → worldbook → previous chapter ending → anchor segments) | consistency supply side; trims cast and worldbook by contract |
novel_write_chapter |
write & commit chapter | gate → mechanical check → ledger → content gate (six dims) → outline-contract metrics → versioning; contract metrics persisted to chapter index |
novel_ledger |
fact ledger (with notes) + point-in-time simulation (status_at snapshot of chapter n / timeline evolution) + foreshadowing plant / payoff / reschedule + cognition ledger |
same-chapter value change rejected; chapter-number-ahead rejected; overdue foreshadowing warned; backfill doesn't alter history; cognition regressions surfaced |
novel_relation |
relation ledger: between-entity state lines (ally/enemy/mentor/trust…): update / query / status_at / timeline |
(A,B) and (B,A) auto-normalised to same pair; same-chapter same-pair same-dimension change rejected; body still using old value → content gate warning every chapter |
novel_arc |
arc ledger: per-character dimension progression (growth/confidence/suspicion…): update / query / status_at / diagnose |
stalled (≥15 chapters without progress) auto-logs a major quality debt; no-move (single entry, ≥20 chapters) warning; zero-token |
novel_quality |
book-level quality debt: view / settle |
three-tier (minor / major / hard); local defects don't block the book; force-passed hard gates must be re-checked |
novel_noai_scan |
eight-dimension anti-AI-smell scan | fully local, zero cost |
novel_audit |
deterministic chapter audit + contract metrics + continuity whole-book consistency + voice voice-gene check + platform Qidian / Fanqie / Jinjiang / Qimao / Feilu five-platform review + censor seven-category sensitivity self-check |
mechanical evidence; platform health table gives actionable fixes |
novel_style |
prose six-axis baseline (μ±σ band) + mood spectrum 12 axes: build baseline / check against (incl. dominant-mood drift) | fully local, zero cost; reports numbers only, no labels |
novel_propose |
proposal / list | edits go through proposals, user applies in panel; old versions never overwritten |
novel_import |
local book import (preview/import/backfill gate replay; file supports .md/.txt/.docx — docx split by Word heading styles and "Chapter N" rules) |
pure-function chapter split; create book + versioned commit; docx uses readBytes byte channel (text channel corrupts binary), rejects zip64, 48MB decompress cap |
novel_export |
export whole book (md/txt + stats) | assembled in version order, written to 导出/ |
novel_diagnose |
golden-three-chapter four-axis diagnosis (hook / opening / conflict / infodump) | deterministic numbers, mechanical and model review separated |
novel_polish |
paragraph-level lesion localization + polish proposal submission | polish also uses proposals, never overwrites old text |
novel_search |
long-work search (build / query / annotate / status): returns chapter number + excerpt + hit ratio | index is a derivative (book/.novel/index.db), re-runnable after deletion; degrades without sqlite |
novel_library |
book-library feed (import/list/read/analyze/delete): decompose structural profiles of benchmark works — chapter-length curve / dialogue density / paragraph rhythm / chapter-end hook rate / high-frequency imagery; compare_book puts your book side by side with numbers |
zero-token fully local; read-only feed, not part of this book's consistency judgment; delete only moves the index |
novel_glossary |
glossary (add/remove/list) | injected with context pack, prevents proper-noun mistranslation |
novel_clone_project |
clone whole book as template | stage reset, proposal and circuit-breaker counters cleared; character cards / worldbook / ledger / foreshadowing / glossary / scene contracts / voice genes all carried along |
Parameter convention: tool parameters deliberately use only scalars (string / integer / boolean); structured data is expressed with delimiter rows (e.g. ledger update
entity|key|value[|note]) — friendly even to small quantized models.
Data layout (one book = one directory in the session workspace)
我的书/
├─ novel.json # machine state: nine stages / approvals / chapter index / cast / proposal index / circuit-breaker count
├─ 大纲/全书大纲.md
├─ 大纲/细纲/第3章.md
├─ 人物/测试角色.md # character card (Markdown, injected verbatim into briefing)
├─ 设定/世界书.json # [{id, keywords[], content, always, priority}]
├─ 设定/术语表.json # [{term, definition}]
├─ 设定/场景契约.json # per-chapter cast / hidden characters / worldbook whitelist / forbidden items
├─ 设定/语言基因.json # per-character: sentence length / logic / catchphrases / never-says / small gestures / register
├─ 正文/第3章-雨夜来客-v2.md # versioned, never overwritten
├─ 账本/facts.json # [{entity, key, value, chapter, note}]
├─ 账本/伏笔.json # [{id, setup, chapter, plan, payoffChapter}]
└─ .novel/
├─ audit.jsonl # full action audit (who did what in which chapter, when rejected)
├─ batch-checkpoint.json # batch-draft checkpoint (updated after each chapter commits / is blocked, status: running→done|partial)
├─ index.db # search index (derivative: re-run novel_search build after deletion)
└─ proposals/P3-xxx.json
A 书库/ directory sibling to the books may also exist at the workspace root — external novel feed, shared across books
(library.json + raw txt; not injected into the context pack, not part of this book's consistency judgment).
All files live inside the session workspace and go through the host's ctx.fs (subject to sandbox and approval policy), so they drop straight into Git / Obsidian.
Uninstalling the plugin does not affect any book data.
Configuration (cordis.patch.yml)
| Item | Default | Description |
|---|---|---|
| minChapterChars | 2000 | per-chapter lower bound (mechanical-reject line, serial-web standard) |
| maxChapterChars | 4000 | per-chapter upper bound (over prompts to split) |
| contextBudgetChars | 6000 | pre-write briefing context-pack budget |
| scanTopK | 8 | max problems listed per dimension in scan report |
| repetitionWindow | 10 | sliding window for cross-chapter repetition detection (compare against previous N chapters) |
| skipPresetDeploy | false | skip preset deploy |
| engine.channels.* | inherit current route | side channels (polish/proofread/annotate/draft) override per channel; long-text channels (polish/proofread/draft) timeoutMs default 600000 |
| engine.channels.*.maxTokens | 0 | 0 = not passed, inherits host's model-calibrated cap (keep default recommended); setting small explicitly gets burned through by the reasoning model's thinking + full rewrite (reports OUTPUT_TRUNCATED) |
| engine.retries | 2 | exponential backoff retries for side-channel calls |
| engine.attachSession | false | whether the side-channel flow writes to the session log; default off (that's what "doesn't occupy the main chat" means) |
The entire
engineblock can be omitted — omitting means "use the current route, retry 2×, don't write session", the most worry-free default. The plugin still loads when the host has no model service; only when a side channel is actually called does it return a readable error.
Dual MCP channel (reuse outside the host)
The same set of novel_* tools (gates / ledger / audit / baselines all live at the tool layer) can also be exposed via stdio MCP to
any MCP client such as Claude Desktop / Cursor — the workspace root takes NOVEL_FORGE_ROOT
(defaults to the process cwd), and one novel = one directory in the workspace:
{
"mcpServers": {
"novel-forge": {
"command": "node",
"args": ["/path/to/dsh-novel-forge/mcp/server.mjs"],
"env": { "NOVEL_FORGE_ROOT": "/your/novel/workspace" }
}
}
}
Zero-dependency implementation (native JSON-RPC 2.0 over stdio); the fs backend outside the host carries the same
containment and version-guard semantics (including the readBytes byte primitive — .docx import works over the MCP channel too).
Troubleshooting FAQ
| Symptom | Fix |
|---|---|
| No "🔨 Forge" tab in the right sidebar | Restart DSH web (bundle is snapshotted at process start) → hard-refresh the browser. The console should show [novel-forge] client apply v… |
| Panel button does nothing when clicked | Hard-refresh first; check the console for [novel-forge] logs. Developers can run the button sweep in npm run preview to locate it |
Polish / proofread returns 503 engine not ready |
The current dsh process has no usable model service: confirm the model route is configured, the plugin is the latest build, and it has been restarted to take effect |
Polish / proofread returns NO_PARENT_AGENT |
The plugin first tries to auto-materialize by the "book's owning session" candidate; if that still fails, send a message in the session that owns the book (so the session agent stays resident), then retry from the panel |
Polish returns OUTPUT_TRUNCATED |
Model output was cut off by the cap. Keep engine.channels.*.maxTokens at the default 0 (inherits the host-calibrated value); don't set it small explicitly |
| Write rejected "outline not approved" | Run novel_outline approve first. To force-skip use force (logged to audit), not recommended |
| Write rejected "chapter N circuit-breaker active" | Triggered by 3 consecutive rejections on the same chapter. Go fix the outline or scene contract; the counter clears automatically once done |
| Mechanical check says "no chapter-end hook" but there clearly is one | The last character of the chapter must be a bare ? / ! / …… — a following closing quote (" / 」) causes a miss |
| Mechanical check says "dialogue ratio 0" | Use Chinese curly quotes "" for dialogue in the body; corner brackets aren't recognized |
| Outline coverage 0% but the scene was written | Coverage matches the outline's "must-write scene" titles lexically (heuristic); align the scene title wording with the outline entry |
| "One-click write chapter" button 501 | By design: writing a chapter requires the briefing + multi-stage commit gates, and can only be done in a session |
"Continue last batch" returns 409 BATCH_RUNNING |
The last batch may still be running: wait for it to finish, or only resume after >10 minutes (anti-double-run guard; a stale checkpoint from a web restart auto-releases after timeout) |
Panel returns 403 FORBIDDEN_HOST |
The access address isn't localhost (you used a LAN IP or domain) — the panel API only allows localhost/127.0.0.1/[::1]; open DSH web with a local address (see "Known boundaries" for the security model) |
After a few dozen chapters the whole turn fails with 400 CONTEXT_WINDOW_EXCEEDED / model token limit |
Session history only grows: every chapter draft (twice — tool call + receipt) plus each briefing pack and all tool receipts stay in history (real-machine evidence: 42 chapters ≈ 490k input tokens). The book is fine — click "Create new session and hand off" in the panel (or copy the handoff summary into a new session) and continue with the next chapter; don't retry the old session. The fatigue banner and the write-chapter receipt raise a hard warning early based on a volume estimate (default: 60% of a 256k budget; the estimate is conservative — real history is only larger) |
Development and testing
npm run setup-dev # symlink the host SDK real package from a DSH checkout into local node_modules
npm run build # src/client/ → lib/client.js (must run after editing client source; npm test runs it automatically)
npm run audit # static self-check: ① called-but-not-imported/declared ② orphan dataset.X reads (also run as npm test pre-step)
npm run preview # generate interactive UI preview preview/forge-ui.html (inlines real build + real host token, opens offline)
npm test # node --test full suite (pure-logic unit + fake-fs full-chain smoke + REST behavior + real-validator output contract + headless behavior; count in the tests badge above)
node scripts/demo.mjs # end-to-end demo: init → outline → write → ledger → scan → propose full flow
Development conventions (pure-logic/io separation, output.schema field-by-field consistency, peerDependencies, client source/build separation, the three-step panel-entry contract, etc.) are in AGENTS.md.
Known boundaries
- Panel REST is localhost-only (security model, since 2026-10-09): the API relies on two browser-unforgeable defenses — the
x-dsh-novel-forgefence header blocks ordinary cross-origin, and theHost/Originallowlist (localhost/127.0.0.1/[::1]) blocks DNS rebinding. The cost is localhost restriction: changing the port is fine (port isn't in the check), an SSH tunnel is fine (Host stays 127.0.0.1), but if you expose DSH web to a LAN and access it from another machine, the Forge panel API will 403 (FORBIDDEN_HOST). The fence gives zero protection against other local processes — that's a design assumption (the threat model is a malicious web page in the browser, not a local process). - The anti-AI-smell wordlist and thresholds are heuristic — they catch only visible symptoms and don't promise a "cure"; the structural metrics (rhythm variance / information dilution) are what make it stronger than a pure wordlist.
novel_auditonly produces deterministic evidence; "model review" happens in the session; the side-channel engine only serves internal chores (polish / proofread / annotate / draft) and doesn't change the line that "audit tools don't call the model".- Batch-draft concurrency cap is 4 (default 1; forced down to 1 when the book has no scene contract); concurrency only applies to drafting, commit is always serial.
novel_searchis lexical retrieval + LLM label enhancement, not embedding semantic retrieval; the index is a single-file sqlite, not stress-tested on 100k-block mega-books;annotateincurs real token cost (default at most 20 blocks per run).- Cross-chapter repetition detection is a sliding window (default previous 10 chapters, adjustable) — repetition beyond the window isn't caught; worldbook recursive activation is capped at 2 rounds.
- Panel TTS uses Web Speech synthesis, with body chunking to prevent Chrome long-text stalls; environments without a speech engine return a readable error.
License
See LICENSE.
Links
More in this category
loopx-project/loopx#dsh-loopx-plugin★ 6537
LoopX, a provider-neutral, local-first state kernel and control plane for long-horizon agents: keeps Goal, Todo, gate, evidence, quota, recovery, and handoff state above DeepSeek Harness, while the plugin bootstraps the CLI and skills, admits bounded same-session continuation, and adds a loopback GoalBar for the exact bound loop.
Q00/ouroboros#integrations/dsh-plugin★ 6308
Config-only bundle that mounts Ouroboros through the DSH MCP client, exposing 36 interview, Seed, execution, evaluation, and evolution workflow tools in DSH.
chuspeeism/dashi-taskboard#deepseek-harness★ 3434
Embeds the active installed Codex Taskboard runtime in the DeepSeek Harness sidebar, using its launcher runtime descriptor instead of a fixed port.
NanmiCoder/dsh-agent-teams★ 2647
AgentTeams multi-agent teams.
EthanYoQ/AI-Novel-Writer#dsh-ai-novel-writer★ 2265
Installs a dedicated AI novel-writing preset and workbench: revisioned local project assets, a compact side drawer, and native approval-gated single-file changes.
Miaotofu01/Study-Mate★ 1307
Your AI learning companion: helping you plan your studies, understand concepts, and build projects to master a subject.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.