dsh-loopx-plugin
loopx-project/loopxLoopX——面向长周期 Agent 的提供商中立、本地优先状态内核与控制平面:在 DeepSeek Harness 执行层之上持久化 Goal、Todo、门禁、证据、配额、恢复与交接状态;插件负责引导安装 CLI 与技能、准入有界的同会话续跑,并为精确绑定的工作循环提供本地 GoalBar。
一个受管的智能体蜂群(swarm):六种专职 DSH 智能体——编排、规划、知识库桥接、开发,以及方案评审与代码评审——把需求转化为严格阶段流水线;每阶段交付必须先以结构化证据自证,下一阶段才放行;评审只读、代码仅评审通过后合并,追加式事件日志支持全程回放与审计,实时看板同步展示。其设计灵感源自 Hermes Agent kanban。
Say one requirement, reply one confirmation — six specialist agents take it from planning to verified delivery. No commands to memorize.
dsh-swarm is a DSH plugin that turns one requirement into a strict, evidence-verified delivery pipeline. An orchestrator (V) decomposes an approved spec into a strictly ordered phase chain (p → (pt?) → w2 → d → dt → w3 → summary); six single-purpose roles (V / P / W / D / PT / DT) run each phase with isolated, permission-gated tool faces; every handoff is machine-verified against an evidence contract; failures recover through idempotent retry and human-gated reviews; and a live Workflow kanban tab streams all state to the browser via SSE. Design inspired by the Hermes Agent kanban.
Swarm mode turns your main session into a team lead: you state the requirement, it clarifies, plans, confirms, delegates and follows through — entirely in natural language, no commands to remember.
/plan: or /openspec: prefixes needed./sms <intent> (e.g. "post current progress to the group"): facts are grounded via kanban lookup, then the body is composed per intent and delivered; -s or wording like "private chat" targets the DM. A bare /sms re-sends the latest completed-chain report and /sms blocked [chainId] the block notice — those two bodies are rendered by system code from kanban facts, never rewritten by the lead. Group and private-chat targets auto-resolve to the single saved target (0 or 2+ targets error out; clean up in dsh-im settings, or pin imDelivery.targetId / imDelivery.dmTargetId, which skips the count check — though with imDelivery.botId empty the pinned target must still belong to the auto-discovered bot, or it errors out).确认 / 开干 / 开跑 / 开始 / go, etc.). The gate is judged by the model, not enforced by a code-level check; vague replies, topic switches, or edit-only feedback count as not confirmed.git checkout/git switch of an existing branch is allowed. Writing code is done by the executor (D) in an isolated workspace by design.Coordinating several agents on one task typically fails in three ways:
dsh-swarm encodes a contract against all three: one responsibility per role, enforced by the permission matrix, tool faces and task-body instructions; every handoff must carry structured evidence or the phase will not close; every stall or review failure lands in a visible, recoverable state — with you (the human) as the final trust anchor. It is built correctness-first: deterministic state machines, append-only event sourcing, idempotent schedulers, and a red-team test suite that replays the event log and rejects any illegal transition (mechanics in Advanced).
| Mode | How you use it | Notes |
|---|---|---|
| Swarm mode (recommended) | Just say the requirement in natural language | No commands to memorize, intent auto-recognized, runs on confirmation |
| Command mode (compatible) | /plan: <requirement> → clarify → /openspec: confirm | Kept for compatibility, functionally equivalent; may be removed in the future — new users should use swarm mode |
Prerequisites: a working DSH runtime (@deepseek-ai/*), Node.js ≥ 22.19 and npm. Optional: a wiki-vault HTTP service (KB features, see Configuration).
From npm (the published tarball ships the built lib/):
dsh plugin --profile web add @joekytc/dsh-swarm
From a source checkout (rebuild first so lib/ matches the sources):
npm install
npm run build # tsc -p tsconfig.build.json + client bundle (lib/client.js)
dsh plugin --profile web add .
From GitHub source:
dsh plugin --profile web add github:joekytc/dsh-swarm— the repository tracks the builtlib/.
Switch the main session's agent preset to Swarm (蜂群模式) — it is installed at $DSH_HOME/.agent-presets/swarm once the plugin is installed.
Example conversation:
You: Add a role-management page to the admin project with CRUD and permission checkboxes
Lead: Let me confirm a few things first —
· Which role fields do you need (name/description/status/…)?
· Permission source: the existing menu tree, or custom?
· Any acceptance requirements, e.g. "deleting a role must not affect linked users"?
You: Fields are name and description, permissions from the existing menu tree, acceptance via TDD
Lead: Checklist saved (six spec sections + repo facts). Reply "confirm" to launch —
I'll spin up the p → (pt) → w2 → d → dt → w3 pipeline.
You: confirm
Lead: Chain created (ch_…), live progress on the kanban tab (Conversation → Trajectory → Kanban).
First phase: Planning (P)…
Six roles, one job each — boundaries enforced by the permission matrix, trimmed tool faces and task-body instructions, so no role creep:
| Role | One-line responsibility | What it never does |
|---|---|---|
| V Orchestrator | Creates phase cards, drives the pipeline, gives guidance on stalls | Never executes |
| P Planner | Reads the spec + repo facts, writes the implementation plan | Never writes code |
| PT Plan reviewer | Read-only review of P's plan (on demand) | Never changes anything |
| W Knowledge officer | Syncs the KB in planning/completion phases | Never touches code/git |
| D Executor | The only role that writes code: implement → verify → commit → push feature branch | Never merges into the target branch itself |
| DT Implementation reviewer | Empirically verifies D's delivery (tests/build/typecheck/diff) | Read-only against the repo |
The pipeline (strictly serial within a chain, parallel across chains):
p ──> (pt?) ──> w2 ──> d ──> dt ──> w3 ──> summary
plan plan rev. KB impl impl rev. KB wrap-up
pt appears only when P decides a plan review is needed; d is always followed by an implementation review (dt).All keys are optional; schema lives in src/config.ts. Most users only need the first three — keep the rest at their defaults.
The 「模型链」 (model chain) card in the config panel: every role gets its own model — pick provider, model and reasoning effort per role; roles you leave untouched show 「继承」 (inherit) and follow the default.

| Key | Default | Description |
|---|---|---|
storageDir | $DSH_HOME/storages/kanban | Event log (events.jsonl), orchestration state, per-task workspaces, dispatcher.log. Value must use the unquoted !!js dshHomePath("storages/kanban") form — quoting degrades it into a literal string |
wikiVault.baseUrl | '' (empty) | wiki-vault HTTP service for KB reads/writes — required for KB features; set to your own server |
wikiVault.pagePrefix | projects/ | Namespace prefix for generated wiki pages |
roles.models.<role> | {} | Per-role model: { provider, model, reasoningEffort?, fallbacks?[] } |
roles.models.<role>.reasoningEffort | high | Default reasoning effort for all roles |
roles.models.<role>.fallbacks | [] | Silent fallback candidates (audited via [model-fallback] comment) |
dispatcher.staleTimeoutSeconds | 14400 | Heartbeat timeout; running task without heartbeat is reclaimed |
dispatcher.maxRetries | 3 | Failure retries before circuit → blocked(gave_up) |
dispatcher.heartbeatIntervalSeconds | 300 | Watchdog heartbeat period |
dispatcher.maxProtocolViolations | 2 | Protocol-violation guardrail: once consecutive violations reach this many, the next one is final (gave_up) |
dispatcher.maxReworksPerRole | { pt: 3, dt: 3 } | Max review rework rounds before review/gave-up + [review-final] |
prefixRoutes.plan | /plan: | Command-mode phase-0 planning prefix |
prefixRoutes.openspec | /openspec: | Command-mode approve-and-execute prefix |
prefixRoutes.learning | /learning | Lessons / retrospective prefix |
prefixRoutes.send | /sms | Free-delivery prefix (DM with -s) |
memory.enabled | true | Memory recall index; false makes planning_memory_recall return a disabled notice |
memory.maxIndexEntries | 8 | Max recalled memory entries (1–20) |
ui.enabled | true | Declared switch; not consumed yet — the tab registers unconditionally |
ui.contentMinWidth | 715 | Declared lower width bound (px); not consumed by the client yet — the tab follows the host conversation width |
ui.contentMaxWidth | 780 | Declared upper width bound (px); not consumed by the client yet |
ui.sseHeartbeatSeconds | 20 | SSE heartbeat interval |
gates.enabled | true | TDD measurement gate; false → silent skip (no event) |
gates.timeoutMs | 600000 | Per-command gate timeout (ms), SIGKILL at the deadline |
gates.forbidden | ['rm -rf /', 'git push'] | Command-blacklist substrings (defence in depth) |
evidenceReplay.enabled | false | L3 replay of model-written commands — not a sandbox, see Issue evidence check |
evidenceReplay.timeoutMs | 600000 | Per-replay command timeout (ms) |
evidenceReplay.allowPrefixes | ['npx --no-install vitest', 'npm test', 'npm run build', 'npm run typecheck', 'tsc', 'eslint'] | Allowlisted tool prefixes (word-boundary match) |
imDelivery.enabled | false | WeCom delivery via dsh-im (W3 wrap-up / chain blocked / review gave-up) |
imDelivery.botId | '' | Empty = auto-discover the only wecom bot |
imDelivery.targetId | '' | Empty = auto-discover the only saved group target |
imDelivery.dmTargetId | '' | DM target for /sms -s |
imDelivery.fallbackBotId | '' | Bot used when preset-based matching finds nothing |
reviewEngine.mode | delegate | delegate or managed — see Review engine (ocr) |
reviewEngine.managed.provider | '' | Model-chain provider id used in managed mode |
reviewEngine.managed.model | '' | Model-chain model id used in managed mode |
wikiWritePresets | ['swarm', 'kanban-w', 'ptc'] | Presets allowed to call wiki_write (page paths are still whitelisted by namespace) |
Implementation reviews (the in-chain DT phase and standalone reviews) are powered by open-code-review (ocr), with two modes switchable in the web config panel under 「Swarm 配置 → 评审引擎(ocr)」 (Swarm config → Review engine (ocr)):
| Mode | How it works | Notes |
|---|---|---|
| Delegate (default) | ocr only outputs the review scope and rules; DT reviews each file with its own model | Zero API keys, works out of the box |
| Managed | ocr runs the full review with your chosen provider/model and returns normalized findings in one shot | For large change sets; delegate mode hints at switching past 50 files (a hint only, never auto-switched) |
The 「评审引擎(ocr)」 (review engine) card in the config panel: with ocr installed, choose the mode (managed / delegate) plus provider and model, then click 「应用到 ocr」 (Apply to ocr) to wire it up.

npm install -g @alibaba-group/open-code-review in a terminal, then verify with ocr --version.projects/<repo>/reviews/<topic>-<date>/ — remote KB mode only; with the default empty wikiVault.baseUrl (local mode) no wiki page is written.Read-only in practice: bash/run_code writes and wiki writes outside the reviews namespace are blocked by the guard, while the fs write/edit tools are not blocked in a standalone DT session.
dsh-managed); the API key is resolved from the dsh model config and written into ocr, never shown in plain text in the panel; if resolution fails it degrades gracefully and points you to a manual ocr config provider in a terminal;Official docs: Installation · Model configuration · Delegate mode
human).evidenceReplay.enabled is turned on.Mechanics and implementation details below — regular users can skip.
Six roles are dispatched by the scheduler as one-shot agent sessions (deterministic session id kbn-<taskId>; a retry resumes that same session — resumeSessionId is always null in the current implementation — while a rework task starts its own kbn-<reworkId> session). Each role-agent session is bound to exactly one task (boundTaskId) and gets a trimmed tool face. V is the exception: a chain-scoped orchestrator session (kbn-v-<chainId>) with no boundTaskId.
| Role | Alias | Responsibility | Tool face (highlights) |
|---|---|---|---|
| V | Orchestrator | Drives the phase machine, creates one card per phase, posts [blocked-review] guidance on stalls. Never executes. | kanban_create + task tools + spec view |
| P | Planner | Reads spec + repo facts (incl. read-only self-checks), writes an OpenSpec implementation plan, opts into PT via pt_decision.needed. Never executes. | Task tools + spec view, read-only (writes only openspec/changes/) |
| PT | Plan reviewer | Read-only review of P's plan (requirements alignment, completeness, logic). Outputs verdict + issues. | Task tools + spec view, read-only ToolGuard |
| W | Knowledge officer | W2/W3 KB sync (w:kb). Never touches code/git. | Task tools + wiki_search/read/write (remote) / skill→llm-wiki (local) + prefetch_file/prefetch_external/prefetch_kb + read-only spec view |
| D | Executor | The only role that writes code: worktree → implement → verify → [AI-GEN] commit → push feature branch (merging into the spec-declared target branch is done by the system only after DT passes). | Task tools + wiki read + bash/fs/run_code (full dev) + subagent (spawn/fork/list-agents) + goal |
| DT | Implementation reviewer | Empirically verifies D's work (test/build/typecheck/diff/git + open-code-review), writes review page to KB. Read-only against the repo. | Task tools + wiki read/write (review namespace) + ocr_review + bash/fs/run_code, read-only ToolGuard |
can(action, actor, task, { boundTaskId }) in src/domain/permissions.ts.
"Bound" means the actor is the role agent session spawned for that exact task
(boundTaskId === task.id and, for complete, also actor === task.assignee).
| Action | V | P | W | D | PT | DT | Human | System |
|---|---|---|---|---|---|---|---|---|
| create-chain / create-task | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| claim | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| complete | ❌ | bound | bound | bound | bound | bound | ✅ (GUI) | ✅ |
| block | ❌ | bound | bound | bound | bound | bound | ✅ | ✅ |
| heartbeat | ❌ | bound | bound | bound | bound | bound | ❌ | ❌ |
| comment | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| unblock | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| archive | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| spec-approve | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| spec-edit | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| spec-attach | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| update-title | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| delete-chain | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| wiki-write | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ (review ns) | ❌ | ❌ |
| wiki-read | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| prefetch | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| audit-confirm | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| create-rework-task | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| reopen-chain | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| waive-review | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
Key guarantees (two):
kanban_show/kanban_chain/kanban_list/kanban_comment plus the human-recovery pair
kanban_reopen_chain/kanban_waive_review — together with spec_card_view, kanban_route,
the planning_* tools and sms_send; never kanban_create/kanban_complete/kanban_block.
Chains/specs are created only via swarm-mode intents or /plan:+/openspec:; the GUI observes
and mutates task state but never creates chains or tasks — "who decided to run what" stays
explicit and auditable.projects/<repoSlug>/<chain>/review/ namespace by a ToolGuard on top of
the matrix; and no role agent can approve specs, unblock, confirm audits, waive a
review or reopen a chain — those are human trust anchors; system handles only
mechanical bookkeeping.Each phase's handoff must carry the keys its downstream actually reads
(src/domain/delivery-contract.ts). Missing delivery keys block the W/P card
immediately and admit no human exemption; the D/PT/DT evidence gates instead reject
complete with an error (the card stays running) and are human-exempt. The
orchestrator never builds a downstream card on a blocked parent:
| Card | Required handoff keys |
|---|---|
W2 / W3 (w:kb) | kb_url + page_path — non-empty is not enough: with a configured wikiVault.baseUrl the kb_url must start with it (and must be exactly '' in local mode), while page_path must sit in the allowlisted namespaces (wiki/** locally) |
P (p:openspec) | artifacts_path + pt_decision (needed boolean required; when needed: true, reason is required) |
D (d:execute) | changed_files + (commit_hash or push) — hasDeliveryEvidence; branch (feature branch) is not a delivery key, but when the TDD gate actually runs the tests it checks the branch and bounces the card if it is absent or mismatched; tdd (test_files or skipped.reason, XOR) |
| PT / DT | review_evidence (schema-valid) — validateReviewEvidence |
D completes only with tdd — test_files (with test_first) or skipped.reason
(XOR, delivery-evidence.ts). DT's review_evidence must carry tdd; whenever
tdd.test_files are declared the runner must be vitest (test.runner), and on a
pass verdict test_first === true must hold (review-evidence.ts). This makes
"tests actually ran, and were written first" a machine-checked property rather than
a claim.
A declaration that disagrees with the diff no longer passes the gate silently
(three silent-skip modes remain, emitting no event: gates.enabled=false, a handoff
without worktree_dir, and non-d:execute cards):
task/gate-skipped event — a declared tdd.skipped is allowed when the diff is pure docs/config, or when the diff cannot be computed (conservative alarm-skip); both leave the event as an audit trail. Otherwise the gate bounces the card back (same session, agent fixes and re-completes).test_files with no test-file change in the diff (stale-test h