dsh-web-search-button
Direct web search button and /search slash command plugin for DeepSeek Harness
- Stars
- 1
- Language
- TypeScript
- Created
- Sep 5, 2026
- Updated
- Sep 8, 2026
Introduction
DSH Web Search Button (dsh-web-search-button)
Direct web search button and /search host-level slash command plugin tailored for DeepSeek Harness (DSH) Desktop.
This plugin allows you to directly invoke the built-in host-level web search service (ctx.web) from the conversation input bar without routing through the LLM, returning real-time web search results formatted with titles, snippets, dates, and markdown source links with 0 Token overhead in ~1 second!
🎯 Why This Plugin?
- Overcome Knowledge Cutoffs Instantly
All LLMs have knowledge cutoffs. Standard agent search requires prompting the model, waiting for multi-step reasoning, invoking tools, and synthesizing output—often failing due to prompt drift or proxy instability. - Zero LLM Overhead
- Traditional Agent flow: User prompt ➔ Model thoughts ➔ Call
web_searchtool ➔ Wait for tool result ➔ Model summarizes. Costs thousands of tokens and 10–20 seconds. - This Plugin flow: User prompt ➔ Click button ➔ Direct host execution of DeepSeek Web Search ➔ Results injected immediately. Takes ~1 second, consumes 0 LLM tokens!
- Traditional Agent flow: User prompt ➔ Model thoughts ➔ Call
- Seamless Native UI Integration
Mounts cleanly in the input bar left slot (conversation.input.left), matching DSH--dsw-*design tokens, supporting dark/light mode, live draft detection, and loading states. - Solving Model Laziness & Hallucination (Why a Persistent Toggle?)
“If some agent models can occasionally invoke search on their own, why is a dedicated toggle switch needed?”- Eliminating Cognitive Overconfidence: Without explicit constraints, LLMs frequently hallucinate answers based on outdated pre-training cutoff data instead of actively initiating web retrieval.
- 100% Deterministic Behavioral Constraint: Switching to the 【Lit Up】 state injects a strict system-level prompt constraint, ensuring the model MUST search the web before answering any temporal, factual, or technical query.
- Frictionless Control: Click again to 【Dim】, instantly reverting to normal conversational mode without disrupting text input.
🖥️ UI Mockup
┌─────────────────────────────────────────────────────────────────────────────────┐
│ │
│ User: /search Latest AI model announcements 2026 │
│ │
│ 🌐 **Web Search Results** (Direct Web Tool Execution): │
│ Query: `Latest AI model announcements 2026` │
│ │
│ ### Sources: │
│ 1. [Gemini 3.8 Flash Launch](https://...) (2026-09-02) │
│ > Google officially releases Gemini 3.8 Flash with tool improvements... │
│ 2. [Claude Fable 5.1 Overview](https://...) (2026-06-09) │
│ > Anthropic introduces Fable 5.1 optimized for coding agents... │
│ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ Input: Latest AI model announcements 2026 │
│ │
│ [+] [Standard Mode ▾] [ 🌐 Web Search ] [ Send ⬆]│
│ ▲ │
│ Plugin Search Button │
└─────────────────────────────────────────────────────────────────────────────────┘
✨ Key Features
- 🌐 Dedicated Composer Button: Mounts directly into the DSH conversation input bar via
conversation.input.leftlist slot. - ⚡ Dual Invocation Modes:
- Button Trigger: Detects current composer draft text and dispatches search on click.
- Slash Command: Type
/search <keyword>directly into the input bar and press Enter.
- 🛡️ Pure Cordis Scope Lifecycle:
- Built with
ctx.inject(["slots", "remote.commands", "sessions"], (scope) => ...). - Automatic unmounting and cleanup on plugin reload (HMR) or disable, preventing memory leaks or orphaned DOM nodes.
- Built with
- 🎨 Native Design Tokens: Built with official
--dsw-*CSS variables, perfectly adapting to theme changes.
🏗️ Architecture & Data Flow
【 Client Plane (Browser Window) 】 【 Host Plane (Node Engine) 】
Composer draft: "Latest AI news"
│
(Click 🌐 Button)
│
Read zone.input.draft
│
Via remote.commands channel
Dispatch: "/search Latest AI news" ─────────► Matches ctx.commands("search")
│
Directly calls host service:
ctx.web.search({ query, maxResults: 8 })
│
Formats Markdown & sources
│
Markdown source card injected directly ◄────────────────── Injects into session
into conversation without LLM overhead (command/done)
📂 Project Structure
dsh-web-search-button/
├── src/
│ ├── index.ts # Host-plane TypeScript source (registers /search command)
│ └── client/
│ └── index.tsx # Client-plane TSX source (SearchButton component & scope injection)
├── lib/
│ ├── index.js # Production Host ESM bundle
│ └── client.js # Production Client bundle (wrapped with window.__ModuleLoader__.load)
├── cordis.patch.yml # DSH bundle patch manifest
├── package.json # DSH manifest (dsh.bundle.patch & dsh.client configurations)
├── tsconfig.json # TypeScript build configuration
├── LICENSE # MIT License
├── README.md # English documentation
└── README.zh.md # Chinese documentation
🚀 Installation
Step 1: Clone or Download to Local Disk
git clone https://github.com/swenbo1-web/dsh-web-search-button.git D:/deepseek/dsh-web-search-button
Step 2: Register in DSH Web Profile
DSH Desktop active profile is located at %APPDATA%\dsh-desktop\harness\profiles\web.
-
Create Directory Junction: Run in PowerShell:
New-Item -ItemType Junction -Path "$env:APPDATA\dsh-desktop\harness\profiles\web\node_modules\dsh-web-search-button" -Target "D:\deepseek\dsh-web-search-button" -
Declare Bundle in
profiles\web\package.json: Open%APPDATA%\dsh-desktop\harness\profiles\web\package.json, ensuredependenciesanddsh.profile.bundlesincludedsh-web-search-button(save as clean UTF-8 without BOM):{ "dependencies": { "dsh-web-search-button": "link:D:/deepseek/dsh-web-search-button" }, "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-web-search-button" ] } } }
Step 3: Restart & Refresh
- Right-click DSH in system tray and select Exit, then relaunch DSH Desktop.
- In DSH chat window, press
Ctrl + F5(hard refresh) to load the updated client bundle. - Check Settings → Plugins to see
dsh-web-search-buttonenabled!
💡 Usage
Mode 1: Composer Toggle Button (Lit Up / Dimmed)
- Open any session; the
[ 🌐 联网搜索 ]button is displayed on the bottom-left of the input bar. - Click once ➔ Directly Lits Up (Bright green/accent background + white text + subtle glow):
- Web Search Mode is ON;
- Zero dialogs, zero alerts, zero prompt confirmations!
- All subsequent messages in this session will automatically invoke
web_searchfor real-time information; - Focus is smoothly preserved on the text box so you can continue typing without interruption.
- Click again ➔ Directly Dims (Neutral/muted background):
- Web Search Mode is OFF, returning to normal chat.
Mode 2: Slash Command
Type slash commands directly into the input bar and press Enter:
/search <keyword> # One-shot direct web search
/search on # Enable persistent Web Search Mode (turns button ON / Lit up)
/search off # Disable Web Search Mode (turns button OFF / Dimmed)
🛠️ Deep Dive: Why scope?
In DSH, registering slots directly on the root context (ctx) can cause timing crashes if dependencies aren't ready, and leaves orphaned listeners on disable.
Our implementation uses scoped dependency injection:
export function apply(ctx: Context) {
// Guarded: Cordis dispatches scope only when slots and remote.commands are ready
ctx.inject(["slots", "remote.commands", "sessions"], (scope) => {
scope.slots.inject("conversation.input.left", () =>
scope.slots.register(
{
name: "conversation.input.left",
id: "web-search-button", // List slots require unique id & order
order: 10,
inject: (sessionId) => ({ ... })
},
SearchButton
)
);
});
}
Binding the button's lifetime to scope ensures clean, garbage-collected unmounting upon reload or disable.
📄 License
MIT © swenbot