dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH plugin: a dedicated agent that plans exact Git commits from real repo state, binds approval to an immutable plan revision, and commits via a restricted, verified git executor.
A dedicated Git commit planning and execution agent for DSH.
The agent reads real repository state, proposes a small set of logically
coherent commits, shows the user an exact per-commit preview, and — only after
the user approves one specific plan revision — runs a restricted git executor
that stages exactly what was approved and verifies the result.
Planning and execution have different authority. Conversation never grants git write access.
GitLens entry ──▶ dedicated native session ──▶ status/diff analysis
│
plan revision + digest
│
user previews & approves (one revision)
│
restricted executor: exact stage → verify tree → git commit
│
report commits / partial failure / reconcile
| Phase | State |
|---|---|
| P0 — host API verification | done, evidence in docs/P0-VERIFICATION.md |
| P1 — interactive planning (no real commit) | done: dedicated session composition, status/diff/read tools, whole-file plans, versioning, exact preview, old-revision invalidation |
| P2 — approval + execution loop | done: exact approval binding, executor, real add/commit, cancel, partial failure, hooks, reconciliation |
| P3 — hunk-level splitting, chat approval protocol | not started (by design) |
| GitLens button + DiffPane plan preview | implemented in src/client/ against the delivered better-sidebar seams; the boot graph carries the entry and the bundle is served — the browser render itself is not yet verified (see docs/P0-VERIFICATION.md §8.7) |
| Live DSH host integration | verified twice on 2026-09-14 (headless profile, scratch DSH_HOME): bare mount with inject: ['tools'] only, restricted 8-tool surface, guard denial, session visibility, uninstall disposal and a real commit — see docs/P0-VERIFICATION.md §7. The two rounds found and fixed three defects (missing lazy agents lookup; followup payload shape; default dataDir ignoring DSH_HOME) |
| Agent preset registration | implemented: the plugin registers itself as the git-commit agent preset through the host's ctx.agentPresets.register() (the same API the declarative @deepseek-ai/dsh-agent-preset plugin wraps), so it appears as a card in Settings → Agent presets; dedicated sessions bind that preset (meta.agentPreset + agentPresets.mount, the webhook session creator's pattern). Not yet verified on a live host (see Preset registration) |
122 automated tests pass against real, isolated git repositories (npm test).
The command palette includes 打开远程仓库 / Open Remote Repository for the
active session, preferring its selected GitLens repository/worktree when available,
including clean checkouts. It reads fetch remotes through an authenticated,
session-scoped read-only endpoint, without contacting the remote or changing Git
state. origin opens directly; a single other remote also opens directly. If
there are multiple remotes without origin, the new tab presents a link chooser.
HTTP(S), git://, ssh://, and SCP-style git@host:owner/repo.git addresses
are converted to HTTPS repository pages. Credentials, query/fragment, and the
terminal .git suffix are removed; SSH/Git transport ports are not reused as web
ports. Local paths and unsupported transports are excluded. This opens the
configured remote (which may be a fork), not an automatically discovered upstream.
Custom SSH host aliases or separate SSH/Web domains need a real web-address
mapping and cannot be reliably inferred by this action. The current endpoint
supports local session repositories; detected SSH execution providers and remote
workspace mirrors fail closed rather than reading Git configuration on the wrong
machine.
With better-sidebar's inspectors feature, the plugin registers two stable
Inspector types: dsh-gca-plan (a publication snapshot) and dsh-gca-plans
(the recent-plan entry). The recent list uses the authenticated read-only
/git-commit-agent/api/plan-index route; it supports workspace-path filtering
and pagination without returning historical patches in list responses. The
current store keeps worktree paths rather than a separate registered workspace
identity, so the filter currently matches an exact worktree path.
明细 and 查看差异 on a transcript card navigate to the exact commit in one Inspector, rather than expanding a second copy of its details. Overview, commit index, dependency links, exclusions and revision delta navigate within that page. Diff is the saved publication patch (plain text), never a recomputed worktree diff. Truncated and unavailable historical previews remain explicit.
The source session belongs to the resource identity, not to its display seat. Opening a resource does not activate or resume its source session. Stable kinds allow the sidebar to restore references before any transcript mounts. Reopening the same revision updates its location; opening another revision uses a distinct resource id. Inspector mode does not poll full historical payloads or auto-open background publications; explicit navigation and list refresh never steal focus. Viewing is read-only and never constitutes approval.
Older sidebar installations without inspectors retain the session-scoped
native Plan tab fallback below (they cannot provide the no-session Inspector).
The prepare-plan transcript card now has 在侧栏查看完整计划. The page shows commit groups, the complete multi-line message, rationale, dependencies, file status/layer/rename/binary markers, stats, exclusions, warnings/blockers and the full revision/digest identity. Each group retains 查看差异. This is a read-only publication snapshot, not an approval surface; it remains available after an approval decision or execution.
While a dedicated commit session is mounted in the native right sidebar, the
client polls the plugin's authenticated, read-only plan route every two seconds.
The first successful response on initial load, every session switch or remount
is a historical baseline: it registers the pages without revealing them. Only
new identities seen subsequently in that same mounted session reveal a page.
Hidden browser documents do not reveal new plans. No private session event
source, conversation pagination hooks, approval listeners or apply_plan calls
are involved.
Native sidebar params/meta are transient and native opens ignore a seed's id.
Each revision therefore registers a hidden, stable tab kind encoding its session,
task, plan, revision and digest; repeated opens focus that same native page.
Payloads are stored atomically in private plugin presentation files and recovered
through /git-commit-agent/api/plan; legacy publications are recovered from the
session's durable successful prepare-tool results through a public read handle.
Recovery never recomputes patches from today's worktree. Missing historical
payloads are reported as unavailable, not invented. The sidebar layout itself
is managed by the host: if refresh collapses it, reopen the historical card.
Run npm run build for host artifacts. This package's hand-written client module
is already its served artifact; a browser refresh loads it. A running host must
reload/restart its existing plugin instance to mount new host HTTP routes (a
browser refresh alone cannot replace host code). Do not start another server to
update the original GUI. Authenticated browser rendering remains a separate
manual check when this agent has no browser session/cookie.
The command palette offers two independent actions (search commit / 提交):
Both actions require the current main-view session to belong to a registered workspace. Input mode refuses to replace existing text, inline references or attachments, or a composer in a non-plain admission phase; send/clear the draft first. Existing session capabilities are preserved, not replaced with a new tool surface (including when the current session is already a dedicated commit agent).
agentOptions in the cordis row, or make sure the
profile has a default model selected; otherwise the first turn ends at prompt
assembly with prompt variable "{{model}}" has no value.$DSH_HOME/git-commit-agent
(DSH_HOME is respected; falls back to ~/.dsh/git-commit-agent). Point
config.dataDir elsewhere if that is not writable — an unusable path fails
loudly as DATA_DIR_UNAVAILABLE before any session is created.resume to work after a restart.dsh plugin --profile <name> add dsh-git-commit-agent
The bundle mounts itself through dsh.bundle.patch in package.json and
cordis.patch.yml. See that file for the optional config block
(dataDir, lockDir, agentOptions).
The plugin mounts with inject: ['tools']. The agent registry is used lazily;
calling the dedicated-session API on a host without one fails with a clear
INTERNAL error instead of degrading silently.
On a host with the agent-preset registry (agentPresets service), the plugin
registers itself as the git-commit agent preset from apply():
ctx.agentPresets.register({
id: 'git-commit', // COMMIT_AGENT_PRESET_ID
name: 'Git Commit Agent',
order: 30,
plugins: [{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' }],
})
This is the same API the declarative @deepseek-ai/dsh-agent-preset plugin
row wraps (PresetDefinition), just invoked from the plugin's own mount so the
definition cannot drift from this plugin's code. What it buys:
ui-agent-preset AgentPresetSection): viewable (its composition YAML) and
selectable as the default — with zero client-side changes;setup calls
agentPresets.mount(agentCtx, 'git-commit') and the session header records
agentPreset: git-commit, exactly like DSH's webhook session creator.The composition supplies only the official native filesystem tools. Git capabilities
arrive from this plugin's host-plane installation (agent/created +
session-git-commit- prefix + restrict/guard), so the preset must not
declare dsh-git-commit-agent as a composition row — that would mount a second
plugin instance inside the preset scope (a second service, shared store, races)
and fail the registry's root-realm service-leak audit, marking the card broken.
Registration waits for the registry through Cordis
ctx.inject(['agentPresets'], callback); patch row order does not guarantee
service readiness. The dependency child owns unregistration, including teardown
while the registration promise is pending. Minimal embedders without inject
retain the one-shot lookup fallback. A successful registration logs
dsh-git-commit-agent: preset git-commit registered.
Current limitation: selecting this preset for an ordinary session does not install the Git-specific tools or the dedicated authorization scope: that installation still requires the dedicated session-id prefix. The ordinary session receives the preset's native filesystem tools under normal host policy. Do not treat this entry as a complete, independently usable default agent preset. A future composition entry must supply the tools and prompt by preset scope rather than by session id.
Both session entry paths (the GitLens button and the host startDedicatedSession
API) seed a prompt in one language, chosen from a single durable preference:
promptLanguage
(zh | en | follow-ui) on its own profile entry — the locale-preference
template; the preset-card Configure button writes it through configForms
with no custom persistence;follow-ui delegates to the active UI locale; a pinned value wins;en; the client plane falls back to its historical Chinese default only when
neither a preference nor a UI locale is available;commit_agent_prepare_plan) and require every user-facing message —
analysis, plan explanation, suggested commit messages and progress reports —
to be presented in the prompt's language (中文 for zh, English for en),
so the session reads fluently in the chosen language even though the third
planes (button labels, approval copy) keep their own translations.Implementation: src/shared/config.ts owns the shared resolver, src/host/session.ts
builds both languages (default en), src/client/ mirrors the same rules.
Both fields become editable through the preset-card Configure button
(settings.agentPreset.card.action slot).
Both session entry paths (the GitLens button and the host startDedicatedSession
API) can pin which LLM model the dedicated session runs on, chosen from the
host's model list (the same provider-grouped catalog the settings models
page renders):
defaultModel
({ provider, model }) on its own profile entry; the preset-card Configure
dialog renders a picker populated from remote.session.modelCatalog() and
writes the exact route through configForms;agentOptions row or the host default model then applies;defaultModel (user's explicit
choice, from the Configure dialog) → deployment agentOptions row → host
default. The stored route overrides only the row's provider/model; other row
options (reasoningEffort, maxTokens) are preserved;remote.session.selectModel — the same durable per-session
selection the composer model seat installs — so the first prompt's request
header is built with it; the host API path passes it as agentOptions;Implementation: the schema field and the admission-time resolution live in
src/host/plugin.ts (resolveAgentOptions) and src/host/settings.ts (schema),
src/shared/config.ts owns the tolerant validator, and src/client/ owns the
catalog picker and the selectModel application.
Commit sessions declare the Git tools plus native read and edit from their
first model request. commit_agent_request_edit changes host-owned authorization,
not tool schemas or system prompts, preserving the unchanged request prefix for
provider prompt caching (actual cache hits depend on the provider).
When planning reveals a necessary correction:
commit_agent_request_edit with exact repository-relative existing file
paths and a reason; the host asks the user through its approval service.read before edit, even if commit_agent_read_files already read
the file. Only explicitly approved files can be edited.action=status lists grants; action=revoke clears them. Grants are ephemeral,
scoped to the current agent session, and never restored after restart/disposal.
Editing authorization never approves a Git commit. An authorized edit attempt
invalidates earlier ready/draft plans and their approvals, conservatively even
if the edit fails or is cancelled. Native operations and Git execution share an
in-process worktree lease, held through asynchronous completion.
This plugin reuses the host's native tools; it does not implement an editor or
bypass filesystem policy. The deployment must provide inherited native read
and edit, a confined filesystem provider, and fs-observation-policy. Native
edits remain subject to those host policies. Missing tools fail installation
closed; missing scoped execution interception prevents edit authorization.
No shell, write, network or delegation capability is added.
Both native reads and edits reject out-of-worktree paths, Git metadata, sensitive credential files, symlinks and non-regular/missing files. Permission escalation arguments are rejected. Validation is repeated at dispatch; the in-process lease does not lock external editors or other processes, and path checks and the host's sandbox containment are not a kernel security boundary; adversarial concurrent filesystem path swaps remain outside this threat model.
| Tool | Purpose |
|---|---|
commit_agent_inspect | the single state tool, mode=status (default: HEAD, branch, index state, index rule, in-progress operations, every pending change with a content-addressed changeId, existing plan revisions) / mode=recent (subjects for style) / mode=reconcile (match one plan revision against real history after a crash or failed execution) |
commit_agent_diff | the single diff reader: the full working-tree diff, one change's diff, or the exact per-commit diff a planned revision would introduce — filterable to one commit (commitId) and one file (path), so a large file can be read in full while the whole-plan preview stays capped |
commit_agent_read_files | the single file reader: bounded contents of specific repository paths; secrets and binaries excluded with a reason |
commit_agent_prepare_plan | prepare a new immutable plan revision; the host validates, computes every expected tree, returns the preview |
commit_agent_apply_plan | submit one revision to the human's plan-review panel and, once approved, execute it; the host verifies approval, digest, snapshot and staged tree |
commit_agent_request_edit | request user authorization for exact existing files, list current grants, or revoke them |
Native read / edit | host-provided file observation and literal editing; edit is locked until a matching user grant exists |
There is no shell, no arbitrary git invocation, no cwd, no file write, no
network and no delegation tool.
The model can propose, preview and explain — it can never approve. Approval goes
through the host's standard approval service (ctx.approval.request()), so the
user answers through a host-owned protocol. The request supplies the short
question as reason, localized question copy as displayReason, and the exact
plan revision's Markdown document as detail. Hosts supporting approval
Markdown detail render it in the ApprovalPanel using the shared safe Markdown
renderer; older hosts show the question only, with the prepare card still
available in the transcript. The document lists per commit the frozen file paths
(status + layer), the --numstat counts and the base→expected trees; revisions
stored before review details existed fall back to change ids with an explicit
note. commit_agent_apply_plan submits one exact revision; only allowed-once
grants approval, and a newer revision revokes an older approval. Only after the
decision does the host execute — the same call returns the execution result.
approvedBy is recorded as user:approval — an audit label, not a cryptographic
identity: DSH has no authenticated in-process user identity, and any in-process
plugin can call the business API's approvePlan directly.
From the command palette or GitLens. When commandPalette is installed,
search for 在当前工作区启动 Commit Agent / Start Commit Agent in Current Workspace
(command ID dsh-git-commit-agent.start-current-workspace). The entry follows the
CMD descriptor protocol: independent localizedTitles for the command and its
Git 提交助手 / Git Commit Agent plugin tag (stable tag ID
dsh-git-commit-agent). This plugin owns and injects its special artwork via
commandPalette.registerIcon(id, Component), composing DSH public primitives.
The start action and shared plugin tag reference
iconId: 'dsh-git-commit-agent.robot-git' (robot + lower-right Git badge).
The fill-prompt action references iconId: 'dsh-git-commit-agent.message-git'
(message bubble + Git badge), distinguishing composing a request from launching
an agent. The palette maintains only its generic built-in icons; these two
special icons are implemented here, not in the palette. Descriptors contain
only serializable IDs, never React components. Registrations are disposed with
the commands and rolled back on partial failure. Older palettes without
registerIcon still receive commands but use their unknown-icon fallback. Both
languages are searchable regardless of the active locale; the palette owns
bilingual display and fuzzy matching. The
single-language title remains a dynamic fallback for older palette versions.
It resolves the workspace owning the current main-view session from the public session and
workspace stores; an ungrouped session or no current session disables the command.
The action rechecks the target and creates the same dedicated session with the
stored model/language preferences, opens it, and seeds/submits the planning prompt.
It does not infer a workspace from palette invocation context, DOM focus or cwd,
and does not preflight Git status: the dedicated agent inspects repository state.
Registration is optional through ctx.inject(['commandPalette']); the child effect
owns command/subscription disposal and store changes refresh comma