@linxin666/dsh-client-ui-task-board 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +59 -20
  3. package/README.zh.md +59 -20
  4. package/lib/client.js +6208 -624
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +4774 -493
  7. package/lib/types/client/PluginSettingsCard.d.ts +7 -0
  8. package/lib/types/client/TaskBoardSettingsCard.d.ts +31 -3
  9. package/lib/types/client/board/ClampedMarkdown.d.ts +3 -0
  10. package/lib/types/client/board/ConfirmDialog.d.ts +4 -1
  11. package/lib/types/client/board/EditTaskModal.d.ts +5 -2
  12. package/lib/types/client/board/LinkSubtaskModal.d.ts +5 -2
  13. package/lib/types/client/board/NewTaskModal.d.ts +4 -1
  14. package/lib/types/client/board/TagManagerModal.d.ts +26 -0
  15. package/lib/types/client/board/TaskBoard.d.ts +10 -1
  16. package/lib/types/client/board/TaskCard.d.ts +14 -1
  17. package/lib/types/client/board/TaskDetail.d.ts +3 -1
  18. package/lib/types/client/board/TaskForm.d.ts +4 -1
  19. package/lib/types/client/board/VerificationReport.d.ts +23 -0
  20. package/lib/types/client/board/card-view.d.ts +113 -0
  21. package/lib/types/client/board/icons.d.ts +32 -0
  22. package/lib/types/client/board/overlay.d.ts +69 -0
  23. package/lib/types/client/board/status-key.d.ts +15 -0
  24. package/lib/types/client/board/task-markdown.d.ts +19 -0
  25. package/lib/types/client/index.d.ts +23 -5
  26. package/lib/types/client/locales.d.ts +168 -1
  27. package/lib/types/client/native-panel.d.ts +6 -20
  28. package/lib/types/client/plugin-card-seat.d.ts +5 -0
  29. package/lib/types/client/schedule-zone.d.ts +50 -0
  30. package/lib/types/client/seats.d.ts +18 -0
  31. package/lib/types/client/service.d.ts +32 -0
  32. package/lib/types/client/settings-form.d.ts +16 -0
  33. package/lib/types/core/controller.d.ts +46 -11
  34. package/lib/types/core/extension.d.ts +283 -0
  35. package/lib/types/core/handover.d.ts +13 -1
  36. package/lib/types/core/issue-form.d.ts +16 -0
  37. package/lib/types/core/poll-cadence.d.ts +31 -0
  38. package/lib/types/core/tasks.d.ts +228 -17
  39. package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
  40. package/lib/types/core/use-cases/task-tag.d.ts +48 -0
  41. package/lib/types/core/use-cases/task-update.d.ts +5 -3
  42. package/lib/types/core/verification-budget.d.ts +52 -0
  43. package/lib/types/core/verification.d.ts +753 -0
  44. package/lib/types/core/workspace-target.d.ts +41 -0
  45. package/lib/types/host/agent-tools.d.ts +1 -1
  46. package/lib/types/host/extension-registry.d.ts +111 -0
  47. package/lib/types/host/llm-dispatch.d.ts +63 -0
  48. package/lib/types/host/verification-gate.d.ts +95 -0
  49. package/lib/types/host/verification-runner.d.ts +139 -0
  50. package/lib/types/host/workspace-evidence.d.ts +53 -0
  51. package/lib/types/host-ledger.d.ts +133 -14
  52. package/lib/types/host-runner.d.ts +55 -10
  53. package/lib/types/host-service.d.ts +205 -11
  54. package/lib/types/index.d.ts +134 -26
  55. package/lib/types/protocol.d.ts +92 -10
  56. package/lib/types/tool-surface.d.ts +71 -0
  57. package/package.json +28 -31
  58. package/src/client/PluginSettingsCard.tsx +9 -0
  59. package/src/client/TaskBoardSettingsCard.tsx +378 -66
  60. package/src/client/board/ClampedMarkdown.tsx +69 -0
  61. package/src/client/board/ConfirmDialog.tsx +7 -3
  62. package/src/client/board/EditTaskModal.tsx +5 -2
  63. package/src/client/board/LinkSubtaskModal.tsx +12 -7
  64. package/src/client/board/NewTaskModal.tsx +168 -21
  65. package/src/client/board/TagManagerModal.tsx +235 -0
  66. package/src/client/board/TaskBoard.tsx +259 -103
  67. package/src/client/board/TaskCard.tsx +214 -81
  68. package/src/client/board/TaskDetail.tsx +289 -43
  69. package/src/client/board/TaskForm.tsx +13 -3
  70. package/src/client/board/VerificationReport.tsx +322 -0
  71. package/src/client/board/card-view.ts +220 -0
  72. package/src/client/board/icons.tsx +89 -0
  73. package/src/client/board/overlay.tsx +189 -0
  74. package/src/client/board/status-key.ts +30 -0
  75. package/src/client/board/task-markdown.module.css +137 -0
  76. package/src/client/board/task-markdown.tsx +106 -0
  77. package/src/client/board-settings.module.css +66 -0
  78. package/src/client/board.module.css +488 -63
  79. package/src/client/index.ts +82 -51
  80. package/src/client/locales.ts +343 -6
  81. package/src/client/native-panel.tsx +18 -2
  82. package/src/client/plugin-card-seat.ts +7 -0
  83. package/src/client/schedule-zone.ts +100 -2
  84. package/src/client/seats.tsx +58 -0
  85. package/src/client/service.ts +94 -0
  86. package/src/client/settings-card.module.css +31 -5
  87. package/src/client/settings-form.ts +27 -1
  88. package/src/core/controller.ts +105 -10
  89. package/src/core/extension.ts +376 -0
  90. package/src/core/freeze-snapshot.ts +2 -2
  91. package/src/core/handover.ts +16 -1
  92. package/src/core/issue-form.ts +19 -0
  93. package/src/core/poll-cadence.ts +41 -0
  94. package/src/core/store.ts +80 -15
  95. package/src/core/tasks.ts +293 -45
  96. package/src/core/use-cases/task-create.ts +30 -9
  97. package/src/core/use-cases/task-parent.ts +3 -2
  98. package/src/core/use-cases/task-schedule.ts +190 -25
  99. package/src/core/use-cases/task-tag.ts +110 -0
  100. package/src/core/use-cases/task-update.ts +13 -7
  101. package/src/core/verification-budget.ts +115 -0
  102. package/src/core/verification.ts +1594 -0
  103. package/src/core/workspace-target.ts +59 -0
  104. package/src/host/agent-tools.ts +125 -38
  105. package/src/host/extension-registry.ts +509 -0
  106. package/src/host/llm-dispatch.ts +87 -0
  107. package/src/host/verification-gate.ts +487 -0
  108. package/src/host/verification-runner.ts +702 -0
  109. package/src/host/workspace-evidence.ts +176 -0
  110. package/src/host-ai.ts +15 -9
  111. package/src/host-ledger.ts +468 -71
  112. package/src/host-routes.ts +15 -2
  113. package/src/host-runner.ts +106 -24
  114. package/src/host-service.ts +546 -40
  115. package/src/index.ts +305 -21
  116. package/src/protocol.ts +226 -28
  117. package/src/tool-surface.ts +95 -0
  118. package/lib/invariant.js +0 -6
  119. package/lib/types/invariant.d.ts +0 -3
  120. package/src/invariant.ts +0 -4
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: ca2285edbe96040109e64f4b1c6c69aea4aea191
6
- README.zh.md: bd038165d02318a0d11c876f487db58786ed76ce
5
+ README.md: 56e7e147e72719ca5607a508bb189c44ddd4937f
6
+ README.zh.md: 2fe78563e70a9374f91eb509601aceefd5938a37
package/README.md CHANGED
@@ -1,8 +1,21 @@
1
- # dsh-task-board — DSH web GUI task board plugin
1
+ # dsh-task-board · Autonomous Task Board & Cron Scheduling for DeepSeek Harness (DSH)
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- A hot-pluggable DeepSeek Harness (DSH) Web GUI plugin with a Host-authoritative task ledger, real DSH session execution, Host cron scheduling, and optional cross-platform idle-sleep protection. It is mounted through `cordis.patch.yml` and the profile mechanism and does not modify DSH source code.
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/@linxin666/dsh-task-board?style=flat-square" alt="Version">
7
+ &nbsp;
8
+ <img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&amp;labelColor=454a54" alt="DSH">
9
+ &nbsp;
10
+ <img src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square" alt="License">
11
+ </p>
12
+
13
+ <p align="center">
14
+ <strong>Autonomous Kanban Task Board & Cron Scheduling for DeepSeek Harness (DSH)</strong><br>
15
+ <em>5-Column Kanban · Cron Background Scheduling · Real Agent Execution · Power Sleep Prevention · Verification Gate</em>
16
+ </p>
17
+
18
+ A hot-pluggable DeepSeek Harness (DSH) Web GUI and official desktop client task automation plugin with a Host-authoritative task ledger, real DSH agent session execution, cron background scheduling, cross-platform idle-sleep protection, multi-agent cascades, and automated verification quality gates. It executes unattended background workflows including automated code reviews, routine health checks, and scheduled pipelines without requiring an open browser window. It mounts cleanly via `cordis.patch.yml` and profiles without modifying DSH core code.
6
19
 
7
20
  - The browser is an asynchronous view; closing the page does not stop Host scheduling or execution settlement.
8
21
  - Every run applies the pinned workspace, agent preset, and permission before sending the task prompt; by default each run creates its own DSH session, and a task can opt into continuing in its previous session instead (issue #1419).
@@ -11,36 +24,43 @@ A hot-pluggable DeepSeek Harness (DSH) Web GUI plugin with a Host-authoritative
11
24
 
12
25
  ## Features
13
26
 
14
- - **Task board UI**: the board is the shell's own center-column panel — like the desktop application's Plugins page, it contributes a row to the shell's official sidebar panel list and a page to the layout's center-column seat, so row geometry, the active highlight, the collapsed rail, the label, and language switching are the shell's to own; the board provides five kanban columns, search, task details, archive/restore, execution history, and links to execution transcripts. Archived tasks are read-only except for restore, delete, and transcript viewing, and cannot run manually or on schedule until restored.
27
+ Cards keep multi-line title and description previews with visible metadata. Previews strip Markdown formatting; task details render description and prompt as safe Markdown without raw HTML or automatic remote images, and content taller than about eight rendered lines is clamped to a fixed height with a fade hint and a "Show more" toggle that expands and collapses it, while shorter content renders untouched with no toggle. Stored task text is unchanged. A bracketed title prefix such as `[Bug]:` renders as a type badge, and the description preview skips issue-form boilerplate (checkbox lists, section headings, empty-field placeholders), preferring a summary section when one exists. A left stripe shows the latest run (running, succeeded, failed, cancelled); a card whose column disagrees with that run is labelled as set by hand. Each column shows the time its cards differ by — created in Backlog/To Do, started while running, settled in Done/Failed — read in the Host time zone. The latest execution session opens straight from the card. Every column header toggles compact single-line cards (Done starts compact, the choice is stored in the browser); Done, Failed and the archive group cards into Today / Last 7 days / Earlier and fold the oldest group once a column holds more than 30 cards, unless a search is active.
28
+
29
+ - **Task board UI**: the board is the shell's own center-column panel — like the desktop application's Plugins page, it contributes a row to the shell's official sidebar panel list and a page to the layout's center-column seat, so row geometry, the active highlight, the collapsed rail, the label, and language switching are the shell's to own; the board provides five kanban columns, search, task details, archive/restore, execution history, and links to execution transcripts. Every column is reachable by hand, by dragging a card or from the detail view's status row: the move writes the column and no execution record, so Done or Failed declares work that finished (or failed) outside a tracked run and Running parks work that is under way without a session, while a card the runner is actually executing refuses every move until it settles. Archived tasks are read-only except for restore, delete, and transcript viewing, and cannot run manually or on schedule until restored.
15
30
  - **Continuation cards (data plane)**: a new task may paste a `<<<FREEZE ... >>>FREEZE` block from a session; it parses into a goal/progress/next snapshot persisted with the task (ledger v3). Cards carry a frozen badge, the detail view shows the full snapshot and freeze time, search covers snapshot text, and archive/restore matches plain tasks. The snapshot reuses the freeze security gate at the protocol layer: sensitive patterns become `[REDACTED]` with a marker, slash-prefixed command lines reject the whole snapshot, and each field is capped at 8 KiB.
16
- - **Handover bundles and the permission confirmation gate**: a continuation card may attach a handover bundle — the pinned execution triplet (workspace / agent preset / permission) plus doc/script references. The bundle's triplet overrides the plain pin fields at execution, and the references ride the prompt as a handover preamble. A binding whose effective permission is above `sessionDefaultPermission` (default `read-only`) is unconfirmed: manual run refuses, cron skips the card and rolls to the next occurrence, and the confirm button in the task detail resolves the binding; any later permission or bundle change re-arms the gate.
31
+ - **Handover bundles and the permission confirmation gate**: a continuation card may attach a handover bundle — the pinned execution triplet (workspace / agent preset / permission) plus doc/script references. The bundle's triplet overrides the plain pin fields at execution, and the references ride the prompt as a handover preamble. A binding whose effective permission is above the deployment's permission baseline — the Host's own default permission preset unless the profile pins `sessionDefaultPermission` — is unconfirmed: manual run refuses, cron skips the card and rolls to the next occurrence, and the confirm button in the task detail resolves the binding; any later permission or bundle change re-arms the gate.
17
32
  - **Claim provenance wrap and source audit**: executing a continuation card (a card with a frozen snapshot) mandatorily wraps the task instruction in a source-declaration template — freeze instant, source session, and an unreviewed-content warning — composed after the handover preamble so the picking-up agent stays wary of stored prompt injection in card text. The session issuing a create/update action is stamped into the snapshot (frozenBy, re-stamped when the snapshot is replaced), and the session issuing a run/rerun lands on the execution record (initiatedBy) together with a captured copy of the freeze provenance; both are visible in the task detail. The initiator is client-asserted audit metadata, not a trust boundary.
18
- - **Task tags (issue #1521)**: a task may carry up to eight labels. A label renders as a colour-toned badge on the card — the tone is hashed from the name, so one label always paints the same way and no colour is stored — the board header gains a multi-select tag filter built from every label in use (including archived tasks), and search also matches label names. A label with an "execution hint" is injected ahead of the execution prompt on every run as a `标签提示` block; a label without one is display and filter only, so an untagged task's prompt is byte-for-byte what it was before the feature. Labels stay editable after the first run: they classify the task and shape the next run, they are not the record of what already ran.
33
+ - **Task tags (issue #1521)**: a task may carry up to eight labels. A label renders as a colour-toned badge on the card — the tone is hashed from the name, so one label always paints the same way and no colour is stored — the board header gains a multi-select tag filter built from every label in use (including archived tasks), and search also matches label names. A label with an "execution hint" is injected ahead of the execution prompt on every run as a `标签提示` block; a label without one is display and filter only, so an untagged task's prompt is byte-for-byte what it was before the feature. Labels stay editable after the first run: they classify the task and shape the next run, they are not the record of what already ran. A **Manage labels** action on the filter row opens the label manager: every label in use with the number of tasks carrying it, a rename that rewrites the label on every card (merging into an existing label of that name and keeping that label's execution hint), and a delete that removes it from every task including archived ones. Both are ledger-wide Host transactions, so several windows looking at one board stay consistent.
19
34
  - **Subtasks and cascade runs**: a task can be given subtasks, either by creating one from its detail view (the form starts from the parent's workspace, preset, permission, and model, and each stays overridable) or by linking an existing task that has no parent yet; a subtask can be detached again once it is not running. The lineage gate lives in the Host: the parent must exist and be on board, a task can never be attached under one of its own descendants, and a tree may not exceed the configured `maxSubtaskDepth` (1 by default, 3 maximum). Running a task also runs its whole subtask tree concurrently — one DSH session per member, all under one run group — and the parent card stays in the running column until its own turn and every subtask have settled; its verdict fails if any member failed, and one failing subtask never stops the others. The board marks parent and subtask cards, the detail view lists the direct subtasks, and the header offers a one-click hide-subtasks filter.
20
- - **Agent Team runs (opt-in per task)**: a task may opt into team execution in its detail view (or through `task_board_create`/`task_board_update`). Running such a task starts ONE session — the Team Lead — and the Host then asks the Agent Teams service to spawn one teammate per subtask inside it, attaching each teammate's session to that subtask's execution. The subtree is flattened into that one Team (only a Lead may spawn); the Lead's prompt lists every teammate by name and points at the team tools, and the Lead's verdict governs the run: a teammate's own completed turn settles its card even while the Team keeps that session alive, and once the Lead's own outcome lands, every member still open is settled with it. A plain cascade prompts the same way but names the independent sessions instead. The mode needs the optional `agentTeams` service: a deployment without it refuses the run by name instead of silently degrading, and a subtask that pins its own above-default permission is refused because a teammate cannot carry that pin. Team runs always mint a fresh Lead session (teammate names are immutable within a Team) and use `teamProvider`. An execution whose session history cannot be read at all is reported failed with the reason after two minutes of polling, so an unreadable session can never leave a card in the running column.
21
- - **Project partition (issue #1536)**: the board header offers a project row built from the deployment's DSH workspaces — "all projects" plus one entry per registered project. Selecting a project narrows the columns to the tasks pinned to it, and tasks with no pinned workspace stay visible under "all projects". Opening the new-task form while a project is open preselects that project as the task's workspace, and "new project…" registers a host directory through the same runtime call the GUI's own add-project uses.
35
+ - **Agent Team runs (opt-in per task)**: a task may opt into team execution in its detail view (or through `task_board_create`/`task_board_update`). Running such a task starts ONE session — the Team Lead — and the Host then asks the Agent Teams service to spawn one teammate per subtask inside it, attaching each teammate's session to that subtask's execution. The subtree is flattened into that one Team (only a Lead may spawn); the Lead's prompt lists every teammate by name and points at the team tools, and the Lead's verdict governs the run: a teammate's own completed turn settles its card even while the Team keeps that session alive, and once the Lead's own outcome lands, every member still open is settled with it. A plain cascade prompts the same way but names the independent sessions instead. The mode needs the optional `agentTeams` service: a deployment without it refuses the run by name instead of silently degrading, and a subtask pin the Lead session does not already carry is refused, because a teammate inherits that session's permission and cannot be narrowed below it — a pin the Lead already covers runs the member at the Lead's authority instead of failing the run. Team runs always mint a fresh Lead session (teammate names are immutable within a Team) and use `teamProvider`. An execution whose session history cannot be read at all is reported failed with the reason after two minutes of polling, so an unreadable session can never leave a card in the running column.
36
+ - **Project partition (issue #1536)**: the board header offers a project row built from the deployment's DSH workspaces — "all projects" plus one entry per registered project. Selecting a project narrows the columns to the tasks pinned to it, and tasks with no pinned workspace stay visible under "all projects". Opening the new-task form while a project is open preselects that project as the task's workspace, and "new project…" registers a host directory through the same runtime call the GUI's own add-project uses. A root task created without a project choice — from the form or through the board's agent tool — inherits the workspace of the session that created it, and a card that still pins nothing runs in the deployment's most recently used workspace instead of the Host's own working directory.
22
37
  - **AI parse of pasted text (issue #1540)**: the new-task form takes text copied from anywhere and has a model turn it into the title, description, and run prompt. The model is one of the deployment's configured models (the same list the task's model pin uses, first entry preselected), and the call runs on the Host behind the board's usual loopback and same-origin fence, with a 45 s budget and a cancel affordance. The draft only fills the form: nothing is created until the task is submitted, and a failed parse leaves what you already typed untouched.
23
38
  - **Collapsible task form**: the create/duplicate/subtask dialog groups its configuration into collapsible regions — task content open by default, then labels, execution settings, run mode, handover, and scheduled runs — and every collapsed region keeps a one-line summary of the values it holds, so the dialog fits without a scrollbar (its body keeps an overflow fallback for very short windows). The agent-preset field is named "Agent preset" and lists the deployment's built-in presets under localized names plus any user preset, with an inherit default that names the preset a run without a pin actually uses.
24
39
  - **Host-authoritative ledger**: tasks, schedules, and execution records live in `$DSH_HOME/task-board/ledger-v2.json`; browser actions become confirmed Host transactions.
25
40
  - **Bounded execution history**: each task keeps the most recent 20 execution records; the oldest runs are trimmed when a new run starts, so ledger size and write cost stay bounded regardless of how often a task has run.
26
41
  - **Real execution**: manual and scheduled runs use the same Host runner, which by default creates a fresh session, renames it, applies the agent preset and `/permission <id>`, then queues the task prompt.
27
- - **Goal-driven runs (on by default)**: unless a card opts out through its detail-view checkbox (the new-task dialog starts checked), the runner queues the task prompt and then arms dsh's built-in `/goal` with that same composed prompt as the objective. dsh's goal-round driver keeps starting continuation rounds in the session until the agent marks the goal complete, and the board keeps the execution in `running` until the goal leaves its active phase: a completed goal settles the run as succeeded, a blocked one fails it with the goal's own reason. The option is per task (`goalRun`, absent means on) and is available to `task_board_create`/`task_board_update` too.
28
- - **Optional session reuse**: a task can opt into continuing in its previous execution's session (issue #1419). Reuse happens only when that session is idle and still present in the runtime roster — the Host then re-applies the pinned permission and model on it and queues the prompt, keeping the conversation title and history; otherwise the run mints a fresh session as before, so an unknown roster or a busy session never blocks a scheduled run.
42
+ - **Goal-driven runs (opt-in per card, on top of the global switch)**: the GLOBAL native `/goal` switch (below, off by default) is the master control; with it on, and only when a card opts in through its detail-view checkbox (the new-task dialog starts unchecked), the runner queues the task prompt and then arms dsh's built-in `/goal` with that same composed prompt as the objective. dsh's goal-round driver keeps starting continuation rounds in the session until the agent marks the goal complete, and the board keeps the execution in `running` until the goal leaves its active phase: a completed goal settles the run as succeeded, a blocked one fails it with the goal's own reason. The option is per task (`goalRun`, absent means off, so a card that never touched it runs one plain turn) and is available to `task_board_create`/`task_board_update` too. The checkbox is always operable: the master switch decides whether the preference takes effect, never whether it can be stated, and it never rewrites the stored value.
43
+ - **Goal acceptance (on by default)**: a task executed in goal form must pass ONE acceptance before `update_goal(action: complete)` may take effect, because the board gates that call on the official tool pre-execute lifecycle instead of trusting the agent's own report. Acceptance runs the default final acceptance of the installed `dsh-llm-verifier` (MIT): the three coding criteria (Specification Adherence, Output Match, Error Signal Detection), a 0.65 threshold applied to the total AND to every criterion, two judge rounds per criterion with the A/B slots swapped and averaged, the task's real trajectory (tool calls, their output, assistant prose) judged against the empty-work baseline, the work must beat that baseline, and — when the deployment records workspace changes — the HOST's own record of what the run changed on disk (file list with added/deleted counts and bounded per-file comparisons) is attached as the prompt's reference context so a described-but-unapplied patch cannot pass. A veto is booked as a quality verdict only when it can be LOCATED: every criterion that makes the run fail must arrive with a structured finding naming the requirement it imposes, the observation that falls short, the difference, and a verbatim citation of the task, the work's own trajectory or the host's workspace record. A rejecting answer with no finding, a generic or empty finding, a citation that does not occur in the reviewed evidence, a citation of the empty-work baseline, and a criterion failed without its own evidence are recorded as an INVALID acceptance instead: they spend no quality budget, do not fail the card, and are bounded separately (two), after which the board stops calling the judge and holds the card for a human. A run whose only records are invalid ends for lack of a matching pass, but with its own terminal reason that states the acceptance was unusable and explicitly denies being an evidence-backed quality veto. A judge that could not see the output because the trace was truncated is treated the same way, because insufficient evidence is not a failure of the work. One execution may accept twice: the first failure returns the total, the per-criterion scores and the located findings to the fixing agent, the second failure ends that execution as failed. Once an execution PASSES and settles, the acceptance-only detail (its findings and structured citations) is removed automatically and only a lightweight audit credential remains — verdict, scores, time, judge route, evidence hash and a one-line problem summary. Nothing outside that execution's own acceptance record is touched, a failed or invalid execution keeps everything a repair or a review needs, the removal is idempotent, and a cleanup that fails is recorded and retried on the next Host start without ever revoking the pass. The budget lives on the execution record, so a repeated completion call, a new goal round, a plugin reload and a Host restart all reuse the same cycle, while a rerun or a scheduled occurrence is a new execution with its own budget. An anomaly (timeout, authentication failure, unparseable judge answer, unresolvable judge route) is recorded separately from a quality verdict and never consumes the quality budget. Exhausting the anomaly budget does NOT fail the card: a judge route that cannot answer is the environment failing, not the work, so the cycle is held open with no further judge call until a user clears the recorded anomalies through `task_board_manage(action: reset-verification)`. An attempt that the acceptance time budget ends before any verdict is recorded as its own stage, spends no budget at all and also fails nothing. Both ceilings are settings (`goalVerificationCallTimeoutSeconds`, `goalVerificationBudgetSeconds`), read live, and a judge call is never opened when the time left cannot hold it. The running column shows executing / verifying / fixing, and each execution's row carries its verdict, judge model and level, count, scores with thresholds, findings, evidence scope and token usage. Forced acceptance also keeps the old fallback paths honest: a paused goal, an unreadable projection, a completed turn and a manual settle can no longer settle a goal execution as succeeded without a matching pass record. A failure carrying acceptance records means the judge answered and the work did not pass, while a failure with no acceptance record at all is reported as its own reason: the gate never opened because the session never called `update_goal(action: complete)` — often a session that narrated completion in prose — so nothing was ever judged and the delivery was never rejected. **A single card can opt out of the gate**: the new-task form and the task detail both carry a Skip acceptance checkbox (off by default, a card-level option like Continue in the same conversation and Start with /goal), and a checked card no longer intercepts its completion claim in goal form. The execution records `applicability: skipped` and the report says the CARD skipped acceptance, which is a different fact from the board-wide switch being off and is reported as such. It applies only to executions started afterwards, so flipping it mid-run never rewrites the rule judging a run already in flight.
44
+ - **Optional session reuse**: a task can opt into continuing in its previous execution's session (issue #1419). Reuse happens only when that session is idle and still present in the runtime roster — the Host then re-applies the pinned permission and model on it and queues the prompt, keeping the conversation title and history; otherwise the run mints a fresh session as before, so an unknown roster or a busy session never blocks a scheduled run. The roster read is taken for the launch itself, so a board that has been idle reuses a session that has been idle all along instead of refusing it.
45
+ - **Idle-aware session polling (issue #1821)**: the Host reads the DSH session roster only while the board has something that roster can decide — an execution to inspect, or a card parked in the running column whose verdict may arrive from a settle this process never saw. An empty board with no running card, no open execution and no enabled schedule polls nothing at all, so an idle Host no longer rebuilds every persisted session row (string conversion, object allocation, one stat per record) on a fixed heartbeat. The cadence that governs a busy board is the `sessionPollSeconds` setting, and a roster read that fails or stays unknown backs off on a doubling delay up to one minute, so a session tree that is down is not hammered at the poll interval.
29
46
  - **Fail-closed pins**: a missing workspace, missing or broken preset, or rejected permission command fails before the task prompt is sent.
30
- - **Host scheduler**: 5-field cron supports `*`, `*/n`, ranges, comma lists, and Sunday `0/7`. Each rule carries its own IANA time zone (the Host's zone by default), and day-of-month/day-of-week follow Vixie semantics: both fields restricted means OR, any other combination means AND. Wall-clock gaps at a daylight-saving transition are skipped, and an ambiguous repeated time fires once, at the earlier instant.
47
+ - **Host scheduler**: 5-field cron supports `*`, `*/n`, ranges, comma lists, and Sunday `0/7`. Each rule carries its own IANA time zone (the Host's zone by default), and day-of-month/day-of-week follow Vixie semantics: both fields restricted means OR, any other combination means AND. Wall-clock gaps at a daylight-saving transition are skipped, and an ambiguous repeated time fires once, at the earlier instant. A rule is either a recurring cron rule — optionally capped by a run limit (unlimited, 1, 2, or any positive whole number) — or a one-shot pinned to a single future instant; the Host counts the executions it actually opens, and a rule that reaches its cap or fires its one-shot stops itself in the ledger rather than waiting for the executed work to disarm it.
31
48
  - **Deterministic recovery**: a running execution with a recorded session is observed after restart; an interrupted start without a session id is cancelled and is not resent.
32
49
  - **Live synchronization**: mutations return a full revisioned snapshot; SSE announces revision, scheduler, and power changes, while reconnect and page visibility recovery fetch a full snapshot.
33
50
  - **Optional idle-sleep protection**: off by default; when enabled it covers every running DSH session, enabled non-archived task-board schedules, and unknown session state.
34
51
  - **System-prompt injection**: the Host registers a `plugin:task-board` section (order 200) through `SystemPrompt.section`, and the task-board settings can disable the announcement without disabling the board. The guidance also reminds agents to close any visible `todo_write` plan before the final answer.
35
- - **Agent tools**: every session gets eight model-facing tools (`task_board_list`, `task_board_get`, `task_board_create`, `task_board_update`, `task_board_set_parent`, `task_board_run`, `task_board_manage`, `task_board_schedule`) that drive the same Host ledger the browser drives, so an agent can list the board, create subtasks, link or detach them, run a cascade, move/archive/restore/delete a card, settle a card the board can no longer observe, and arm its cron schedule from the conversation.
52
+ - **Agent tools**: every session gets eight model-facing tools (`task_board_list`, `task_board_get`, `task_board_create`, `task_board_update`, `task_board_set_parent`, `task_board_run`, `task_board_manage`, `task_board_schedule`) that drive the same Host ledger the browser drives, so an agent can list the board, create subtasks, link or detach them, run a cascade, move a card to any column (declaring it done, failed, or in progress without a run), archive/restore/delete it, settle a card the board can no longer observe, and arm its cron schedule from the conversation.
53
+ - **External outcomes (issue #1826)**: work that is not executed by this Host — an agent outside DSH such as Codex or Claude Code, running its own model — can be recorded as a real run rather than a bare column edit. `task_board_manage(action: 'record-external-outcome', result: 'succeeded'|'failed', initiatedBy: <who>)` appends an execution record carrying the verdict, the caller and an optional summary, and the terminal column is derived from that same record by the same rule a Host settlement uses (`core/tasks.ts` `settledStatus`). The record is marked `external`, never carries a session, and is refused while the card still has an open execution (while this Host is running the card it stays the only authority) and on an archived card. `running` and `cancelled` cannot be recorded this way: those stay Host-only. A manual `move-done`/`move-failed` remains available and still writes only the column — the difference is that this one leaves a record behind, so the column and the run history can never disagree.
54
+
55
+ - **External provider extensions**: the GitHub Issues extension (`@linxin666/dsh-client-ui-task-board-github`, enabled by default) synchronizes GitHub Issues into board cards. It is a provider of THIS board, so its configuration renders inside this plugin's own settings card — the section only appears while the extension is installed, and its repository/credential form only while the extension is on; see [its README](../dsh-task-board-github/README.md).
36
56
 
37
57
  ## Architecture and protocol
38
58
 
39
59
  The board view renders on first open and keeps its local view state when closed and reopened. Host synchronization, scheduling and execution remain active independently of the view.
40
60
 
41
61
  - `src/index.ts` mounts the Host service through the official `@deepseek-ai/dsh-api-gateway`, `@deepseek-ai/dsh-workspace`, and `@deepseek-ai/dsh-host-webserver` SDKs.
42
- - `src/host-ledger.ts` serializes actions and persists `{ schemaVersion: 3, revision, tasks, scheduler, recentRequests }` through a temporary file plus atomic rename.
43
- - `src/host-service.ts` owns cron firing (a one-shot timer armed at the ledger's nearest target through the host's `timer` service, falling back to process timers when the host serves none), missed-trigger skipping, runner launch, restart reconciliation, and power reasons.
62
+ - `src/host-ledger.ts` serializes actions and persists `{ schemaVersion: 6, revision, tasks, scheduler, recentRequests }` through a temporary file plus atomic rename.
63
+ - `src/host-service.ts` owns schedule firing (a single timer armed at the ledger's nearest target through the host's `timer` service, falling back to process timers when the host serves none), missed-trigger skipping, runner launch, restart reconciliation, and power reasons; the ledger itself owns the run budget, so a one-shot or an exhausted capped rule can never be opened a second time.
44
64
  - `src/client/native-panel.tsx` registers the board's sidebar row and center-column page (the official `sidebar.panellist` list seat and the keyed `main` seat) and maintains the center-column exclusivity protocol with the ssh panel.
45
65
  - `src/client/host-api.ts` imports legacy browser data once, submits idempotent actions, and treats Host snapshots as the only confirmed UI state.
46
66
  - Same-origin endpoints are `GET /api/task-board/state`, `GET /api/task-board/events`, and `POST /api/task-board/action`.
@@ -56,8 +76,8 @@ The board is operable from a conversation, not only from the GUI. Each tool driv
56
76
  - `task_board_update` edits content, labels, and execution targets; an empty string clears a target and an empty label array clears the labels.
57
77
  - `task_board_set_parent` links an existing card under a parent, or detaches it with an empty parent id.
58
78
  - `task_board_run` runs a card now (optionally re-running a settled one), cascading over its whole subtask tree; it consumes real API quota and refuses an unconfirmed above-default permission with `confirmation-required`.
59
- - `task_board_manage` moves a card between backlog and todo, archives or restores it, deletes it, or settles a running card the board can no longer observe (recording a cancelled verdict with the caller as the reason and returning the card to todo), with the Host's running-task and subtask guards.
60
- - `task_board_schedule` arms, changes, or disarms a card's cron schedule, including its IANA time zone (an empty `timeZone` clears it back to the Host zone).
79
+ - `task_board_manage` moves a card to any column — the move writes the column and never an execution record, so backlog/todo plan, done/failed declare work that finished outside a tracked run and running parks work that is under way without a session, while a card with an open execution refuses the move until it settles — archives or restores it, deletes it, or settles a running card the board can no longer observe (recording a cancelled verdict with the caller as the reason and returning the card to todo), with the Host's running-task and subtask guards.
80
+ - `task_board_schedule` arms, changes, or disarms a card's schedule — a recurring cron rule (optionally capped by `maxRuns`; `0` clears the cap back to unlimited) or a one-shot at an epoch-millisecond `at` — including its IANA time zone (an empty `timeZone` clears it back to the Host zone).
61
81
 
62
82
  There is deliberately no tool that confirms a permission binding: that gate exists so a human lifts an above-default permission, and an agent able to stamp it would make the gate decorative. An agent that hits `confirmation-required` asks the user to confirm the card in the board UI. Tool calls are attributed: a run records the calling session as its initiator, and a create/update stamps it into a continuation card's snapshot.
63
83
 
@@ -81,6 +101,8 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
81
101
 
82
102
  ## Configuration
83
103
 
104
+ This plugin's settings card groups its options into collapsible sections — the board and its runtime behavior, task acceptance, and the section a provider extension contributes (the GitHub Issues integration). Every section starts collapsed, and one save writes them all.
105
+
84
106
  | Key | Default | Behavior |
85
107
  | --- | --- | --- |
86
108
  | `enabled` | `true` | Enables the Host service and browser board. |
@@ -88,8 +110,15 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
88
110
  | `preventIdleSleep` | `false` | Holds one system idle-sleep assertion while any DSH session runs, any schedule is enabled, or session state is unknown. |
89
111
  | `trustedProxyHosts` | `[]` | Canonical `host[:port]` authorities accepted only through the authenticated loopback reverse-proxy path. |
90
112
  | `proxyTokenEnv` | `DSH_TASK_BOARD_PROXY_TOKEN` | Environment variable containing the reverse-proxy token; the token itself is never stored in plugin config. |
91
- | `sessionDefaultPermission` | `read-only` | The deployment's session-default permission. A card whose effective permission (handover bundle or pin) is above this value requires a human confirmation before it may run; cron refuses unconfirmed cards. |
113
+ | `sessionDefaultPermission` | follow the Host | Explicit baseline for the permission confirmation gate. Unset, the board follows the Host's own default permission preset (what new DSH sessions start at) and falls back to `read-only` when the deployment serves no permission catalog. A card whose effective permission (handover bundle or pin) is above the baseline requires a human confirmation before it may run; cron refuses unconfirmed cards. |
92
114
  | `maxSubtaskDepth` | `1` | Subtask depth limit, 1 to 3. At 1 a task may carry one level of subtasks and a subtask cannot be given subtasks of its own; every extra level multiplies the sessions one run of the root opens. |
115
+ | `sessionPollSeconds` | `5` | How often, in seconds, the Host re-reads the DSH session roster while the board has something to reconcile: a running card or an open execution. An idle board with neither does not poll at all. Range 1 to 300; a shorter interval settles runs sooner and costs the Host more. |
116
+ | `goalRunEnabled` | `false` | GLOBAL native `/goal` switch. While off, every card runs one plain turn and the board never arms dsh's built-in `/goal`; a run uses native `/goal` only when this is on AND the card's own Start with /goal option is checked (that option is off by default). The value is frozen when an execution starts, so a live edit only affects executions that begin afterwards, and a card's stored `goalRun` preference is never rewritten. Such a run is recorded as `goal-disabled`: it is neither reported as accepted nor failed for lacking a completion call. Independent of `goalVerification`. |
117
+ | `goalVerification` | `true` | Goal acceptance for executions this board starts. When on, `update_goal(action: complete)` inside a task execution is refused until an acceptance pass is recorded for that execution. Only goal-form executions are affected: plain chat and a card that did not opt into `goalRun` are untouched. A single card can opt out of the gate on its own with the Skip acceptance checkbox (below). |
118
+ | `goalVerificationModel` | `''` (inherit) | Judge model route as `provider/model`. Blank inherits the HOST model catalog default — never the card's pinned execution model. |
119
+ | `goalVerificationReasoningEffort` | `''` (inherit) | Judge reasoning level. Blank inherits the host default level; a level the resolved model does not declare is never sent, and the fallback to that model's own default is recorded and shown. |
120
+ | `goalVerificationCallTimeoutSeconds` | `150` | Ceiling of ONE judge request of an acceptance, in seconds (range 30 to 600). One acceptance issues six judge requests, so a ceiling too small for the judge model turns a slow but healthy route into a wall of timeouts. |
121
+ | `goalVerificationBudgetSeconds` | `1200` | Total ceiling of ONE acceptance attempt, in seconds (range 120 to 1800), covering evidence rendering and every judge call including retries. Keep it at or above the per-call ceiling times six or the acceptance can never finish; an attempt that runs out is recorded as a budget stop that judges nothing and spends no budget. |
93
122
  | `teamProvider` | `spawn` | Continuable-subagent provider the Agent Teams service composes a teammate from. Only team-mode runs use it; it matches the Agent Teams tool plugin's `freshProvider` default. |
94
123
 
95
124
  Direct browser access remains limited to the DSH loopback origin. For a same-host authenticated reverse proxy, bind DSH Web to loopback, set `trustedProxyHosts`, place a high-entropy token in the environment variable selected by `proxyTokenEnv`, and configure the proxy to replace (not forward from the client) `X-Dsh-Task-Board-Proxy-Token` after it authenticates the request. The proxy Host must be allowlisted, and the browser `Origin` must have that same authority. Restart the Host after changing these composition-level proxy settings.
@@ -98,12 +127,14 @@ On macOS the backend starts `/usr/bin/caffeinate -i -w <host-pid>` and never req
98
127
 
99
128
  ## Data storage and migration
100
129
 
101
- - The authoritative ledger file is `$DSH_HOME/task-board/ledger-v2.json` (the file name is historical); the current document schema is v3, and a v2 document is migrated losslessly to v3 in place on the next Host start. New POSIX files use mode `0600`; Windows inherits the user directory ACL.
130
+ - The authoritative ledger file is `$DSH_HOME/task-board/ledger-v2.json` (the file name is historical); the current document schema is v5, and an older document (v2, v3 or v4) is migrated losslessly in place on the next Host start. New POSIX files use mode `0600`; Windows inherits the user directory ACL.
131
+ - v5 adds the per-execution acceptance block (`verification`). The migration deliberately does NOT stamp one, so an execution opened before v5 keeps settling on its historical verdict and no in-flight run is retroactively judged by a gate that was never armed for it. A malformed block is dropped (that execution is then treated as unverified — fail closed), and an imported execution record never carries one, because a fabricated pass record must not let imported work look accepted.
102
132
  - A v2 to v3 migration failure (structurally invalid task rows) fails closed with an explicit error and keeps the original file untouched; it never restarts from an empty ledger silently. A corrupt or unsupported-schema file is moved to a collision-resistant `ledger-v2.json.corrupt-*` name and the Host starts with an empty ledger plus a visible scheduler error. The corrupt bytes are not overwritten.
103
133
  - On the first upgraded page load for an origin, `dsh.taskBoard.v1` is imported by stable source and request ids. Tasks merge by id, strictly newer browser top-level fields win, equal timestamps keep Host fields, and execution records merge by execution id.
104
134
  - The most recent 256 request ids and SHA-256 action fingerprints are stored with the ledger, so a retried mutation remains idempotent after a Host restart without duplicating full action payloads.
135
+ - An external-outcome execution record adds one optional boolean (`external`). A ledger written before it existed carries none, and the loader neither invents one nor treats an absent field as external, so old documents read back unchanged; a non-boolean value is dropped rather than trusted as provenance. The ledger stays at v5: the field is absent from every existing row, which is exactly the additive change the v5 migration already describes.
105
136
  - Task labels are an optional `tags` field on the task row (`{ name, promptPrefix? }[]`) and needed no schema bump: a v3 document without it loads unchanged, and a malformed list is repaired entry by entry (blanks, repeats, and over-long names dropped, count capped) rather than dropping the task row.
106
- - The goal opt-in is an optional `goalRun` field on the task row and needed no schema bump either: absent means on, an explicit `false` is the opt-out, and a stray `true` is repaired back to absent on load.
137
+ - The goal opt-in is an optional `goalRun` field on the task row and needed no schema bump either: absent means off, an explicit `true` is the opt-in, and a stray `false` is repaired back to absent on load.
107
138
  - The import marker `dsh.taskBoard.v2.hostImported` stores the confirmed Host ledger generation only after import succeeds. A new or recovered ledger generation is offered the retained v1 data again. The v1 localStorage value remains untouched as a read-only rollback copy.
108
139
  - One Host process owns a task-board ledger directory at a time through `$DSH_HOME/task-board/ledger-v2.lock`; a second Host using the same DSH home fails closed instead of concurrently writing the ledger.
109
140
 
@@ -137,7 +168,7 @@ Set `DSH_POWER_SMOKE=1` to opt into the native helper smoke test on Windows, mac
137
168
  2. Create and edit a task; refresh or open a second same-origin tab and confirm both show the same Host revision.
138
169
  3. Run a task with pinned workspace, preset, and permission; confirm a new session appears and the task settles from its `turn/end` history.
139
170
  4. Run a task with the /goal option checked: confirm the session starts a goal and the card stays in `running` across continuation rounds until the goal completes, then settles as succeeded. Uncheck the option and confirm the next run is a single turn that settles from one `turn/end`.
140
- 5. Enable a near-future cron, close all browser pages, and confirm the Host still creates and settles exactly one execution.
171
+ 5. Enable a near-future cron, close all browser pages, and confirm the Host still creates and settles exactly one execution. Then arm a one-shot at a near-future instant and confirm exactly one execution is created and the rule reads as ended; set a recurring rule's run limit to 2 and confirm the third occurrence creates nothing.
141
172
  6. Stop the Host past a cron occurrence, restart it, and confirm the missed occurrence is skipped and `nextRunAt` rolls forward from current Host time.
142
173
  7. Enable `preventIdleSleep`, run a long session, and let the display turn off; after restoring the display, confirm the session continued and the execution settled.
143
174
  8. Disable the setting and all schedules, stop DSH, and confirm the helper exits; on macOS, `pmset -g assertions` should show no display-sleep assertion from this plugin.
@@ -146,7 +177,8 @@ Set `DSH_POWER_SMOKE=1` to opt into the native helper smoke test on Windows, mac
146
177
  ## Known limitations
147
178
 
148
179
  - Missed occurrences during Host downtime, system sleep, or a long pause are skipped and never queued for catch-up.
149
- - A task that is already running skips its due occurrence and rolls to the next cron match; task runs never overlap or queue.
180
+ - A task that is already running skips its due occurrence and rolls to the next cron match; task runs never overlap or queue. A one-shot has no next match: an instant missed because the card was running or the Host was down stops the rule as skipped, and is never replayed.
181
+ - A capped rule counts the executions the scheduler opened, and a manual run never spends the scheduled budget. A launch that fails before any session exists is refunded, so the rule keeps its next occurrence; a run that did reach a session counts even if it then failed.
150
182
  - DST follows the Host local wall clock: a nonexistent spring-forward minute is skipped, and a repeated fall-back minute is not replayed a second time.
151
183
  - Power protection prevents only idle system sleep. It deliberately allows display sleep and lock.
152
184
  - Lid close, manual sleep, hibernation, shutdown, low-battery forced sleep, and enterprise power policy are outside the guarantee.
@@ -159,6 +191,13 @@ Set `DSH_POWER_SMOKE=1` to opt into the native helper smoke test on Windows, mac
159
191
  - A goal run keeps working for as many rounds as the objective needs: every round consumes API quota, and the session stays busy until the goal completes or dsh blocks it (round limit, a rejected round, or a failed queue), which the board reports as a failed execution.
160
192
  - Goal mode needs the runtime's `/goal` command and a command dispatcher. A deployment that serves neither still runs every card as a single turn; the host log reports that the goal was unavailable instead of failing the run.
161
193
 
194
+ - Acceptance spends model requests: one acceptance is three criteria times two rounds (six judge requests), and one execution may accept twice, so a goal cycle that needs its one repair costs up to twelve extra requests on top of the work itself.
195
+ - No cost estimate is shown for an acceptance: this deployment exposes no reliable per-token price for the resolved judge route, so the report lists tokens only.
196
+ - The host-observed workspace-change block appears only when the deployment records changes and the read succeeds; an unavailable service or a failed comparison degrades the evidence to the trajectory alone rather than failing the acceptance.
197
+ - Acceptance is enforced only where it can be: a run that never became a goal run (`/goal` refused or unavailable, or the global switch off) and a teammate execution are not gated, and their record says so explicitly instead of implying they were verified. A global-switch run is marked `goal-disabled` — its own reason, distinct from a refused command — so it is never read as a passed acceptance either. A deployment that serves no model catalog cannot resolve a judge route, so a completion claim is refused and recorded as an acceptance anomaly; once the anomaly budget is spent the card is held open rather than failed, and recovering needs a user to clear the anomalies.
198
+ - The first version always judges with the coding criteria: there is no automatic rubric selection, so a non-coding task is judged by engineering criteria (the settings copy states this).
199
+ - If a third-party verifier (for example the installed `dsh-llm-verifier`) also enables its own automatic acceptance, both judges score the same session independently: this board's acceptance gates completion while the other only steers, and no public SDK interface lets them share one verdict. Turning that plugin's automatic mode off is the only reliable way to avoid paying for two judgments.
200
+
162
201
  ## Telemetry
163
202
 
164
203
  The browser half sends one anonymous install heartbeat per UTC day to dsh-market.com: a random localStorage id plus this package's name, nothing else. The server stores only a salted hash of that id, never IP addresses, and exposes aggregate counts only. See [docs/telemetry.md](../../docs/telemetry.md) for the full contract.
package/README.zh.md CHANGED
@@ -1,8 +1,21 @@
1
- # dsh-task-board — DSH web GUI 任务看板插件
1
+ # dsh-task-board · DeepSeek Harness (DSH) 自动化任务看板与 Cron 定时调度插件
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 一个可热插拔的 DeepSeek Harness (DSH) Web GUI 插件,提供 Host 权威任务账本、真实 DSH 会话执行、Host cron 调度和可选的跨平台空闲睡眠保护。插件只通过 `cordis.patch.yml` 与 profile 机制挂载,不修改 DSH 源码。
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/@linxin666/dsh-task-board?style=flat-square" alt="Version">
7
+ &nbsp;
8
+ <img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&amp;labelColor=454a54" alt="DSH">
9
+ &nbsp;
10
+ <img src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square" alt="License">
11
+ </p>
12
+
13
+ <p align="center">
14
+ <strong>DeepSeek Harness(DSH)官方 Web GUI 与桌面客户端自动化任务管理与长程后台执行引擎</strong><br>
15
+ <em>5 列状态流转 · Cron 后台定时调度 · 智能体真实执行 · 防休眠电源保护 · 任务验收质检</em>
16
+ </p>
17
+
18
+ 一个高可用、可热插拔的 DeepSeek Harness (DSH) Web GUI 与官方桌面客户端任务自动化插件,提供 Host 权威任务账本、真实 DSH 会话自动化执行、Cron 表达式后台定时调度、跨平台空闲睡眠保护、多 Agent 团队级联与自动化质量验收门禁。用户无需常驻浏览器,即可在后台无人值守运行代码巡检、健康检查与定时自动化流水线。插件只通过 `cordis.patch.yml` 与 profile 机制挂载,零修改侵入 DSH 官方源码。
6
19
 
7
20
  - 浏览器只是异步视图;关闭页面不会停止 Host 调度或执行结算。
8
21
  - 每次运行在发送任务 Prompt 前应用钉住的工作区、agent 预设与权限;默认每次新建独立 DSH 会话,任务也可选择改为在上一会话中继续(issue #1419)。
@@ -11,36 +24,43 @@
11
24
 
12
25
  ## 功能
13
26
 
14
- - **任务看板 UI**:看板是 shell 自己的中栏面板——它像桌面版【插件】页那样,向 shell 的官方侧栏面板列表贡献一行、向布局的中栏座位贡献一页,因此行的几何、选中高亮、折叠 rail、标签与语言切换都由 shell 统一负责;看板提供五列布局、搜索、任务详情、归档/恢复、执行历史和执行会话跳转。归档任务除恢复、删除和查看 transcript 外保持只读,恢复前不能手动或定时执行。
27
+ 卡片保留多行标题、描述预览及可见的元数据。预览去除 Markdown 标记,详情中的描述与 Prompt 按安全 Markdown 渲染,不执行原始 HTML 或自动加载远程图片;超过约八行的内容默认按固定高度钳制并带渐隐提示,点「展开全文」展开、可再收起,未超出的内容原样渲染且无切换按钮。任务原文保持不变。标题开头的方括号前缀(如 `[Bug]:`)渲染为类型徽章,描述预览跳过 Issue 表单的模板内容(勾选列表、小节标题、空字段占位),存在摘要类小节时优先取它。卡片左侧色条显示最近一次执行(运行中、成功、失败、取消);所在列与该次执行不一致的卡片标注为手动结算。每列显示能区分卡片的时间——待规划/待办为创建时间、运行中为开始时间、已完成/已失败为结算时间——按 Host 时区读取。最近一次执行的会话可直接从卡片打开。每个列头都可切换紧凑单行卡片(已完成列默认紧凑,选择保存在浏览器中);已完成、已失败与归档按「今天 / 近 7 天 / 更早」分组,列内超过 30 张卡时折叠最早一组,搜索时不折叠。
28
+
29
+ - **任务看板 UI**:看板是 shell 自己的中栏面板——它像桌面版【插件】页那样,向 shell 的官方侧栏面板列表贡献一行、向布局的中栏座位贡献一页,因此行的几何、选中高亮、折叠 rail、标签与语言切换都由 shell 统一负责;看板提供五列布局、搜索、任务详情、归档/恢复、执行历史和执行会话跳转。五列都可以手工切换:拖拽卡片或在详情页点状态即可,只写列、不产生执行记录——「已完成」「已失败」声明工作在受追踪的执行之外结束(或失败),「进行中」则表示工作正在进行但没有对应会话;真正在执行中的卡片在结算前拒绝任何移动。归档任务除恢复、删除和查看 transcript 外保持只读,恢复前不能手动或定时执行。
15
30
  - **续接卡片(数据面)**:新建任务时可粘贴会话输出的 `<<<FREEZE … >>>FREEZE` 冻结块,解析为「目标/进度/下一步」快照随任务持久化(v3 账本);卡片带冻结徽标,详情页可读完整快照与冻结时间,搜索覆盖快照文本,归档/恢复与普通任务一致。快照在协议层复用冻结安全门:敏感模式自动替换为 `[REDACTED]` 并标记、以 `/` 开头的命令行整体拒绝、每字段 8 KiB 上限。
16
- - **交接包与权限确认门**:续接卡片可附交接包——钉住三元组(工作区/agent 预设/权限)加文档与脚本引用。执行时交接包三元组覆盖普通钉住字段,引用以交接前言随 Prompt 下发。有效权限高于 `sessionDefaultPermission`(默认 `read-only`)的绑定处于待确认状态:手动执行被拒绝、cron 跳过该卡并滚动到下一触发点,任务详情中的确认按钮完成人工确认;此后任何权限或交接包变更都会重新武装确认门。
31
+ - **交接包与权限确认门**:续接卡片可附交接包——钉住三元组(工作区/agent 预设/权限)加文档与脚本引用。执行时交接包三元组覆盖普通钉住字段,引用以交接前言随 Prompt 下发。有效权限高于部署权限基线——未钉住 `sessionDefaultPermission` 时即宿主自己的默认权限预设——的绑定处于待确认状态:手动执行被拒绝、cron 跳过该卡并滚动到下一触发点,任务详情中的确认按钮完成人工确认;此后任何权限或交接包变更都会重新武装确认门。
17
32
  - **领卡来源声明包裹与来源审计**:执行续接卡片(带冻结快照的卡片)时,任务指令被来源声明模板强制包裹——冻结时间、来源会话与未经人工审查提示,组合在交接前言之后,使接手 Agent 对卡片文本中的存储型提示注入保持警惕。create/update 动作的发起方会话织入快照(frozenBy,快照被替换时重新盖章),run/rerun 的发起方会话连同冻结来源的捕获副本一起落在执行记录(initiatedBy)上,两者均可在任务详情查看。发起方为客户端断言的审计元数据,不构成信任边界。
18
- - **任务标签(issue #1521)**:每个任务可带最多八个标签。标签在卡片上渲染为带色调的徽章——色调由标签名哈希得到,因此同一标签永远同色,也不需要存储颜色;看板顶栏据此提供多选标签筛选,候选项来自全账本在用的标签(含已归档任务),搜索也会匹配标签名。填了「执行提示」的标签会在每次执行前以 `标签提示` 段注入到执行 Prompt 之前;没填的标签只作展示与筛选,因此无标签任务的 Prompt 与加入该功能前逐字节一致。标签在首次执行后仍可编辑:它们给任务分类、塑造下一次运行,而不是「已经跑过什么」的凭证。
33
+ - **任务标签(issue #1521)**:每个任务可带最多八个标签。标签在卡片上渲染为带色调的徽章——色调由标签名哈希得到,因此同一标签永远同色,也不需要存储颜色;看板顶栏据此提供多选标签筛选,候选项来自全账本在用的标签(含已归档任务),搜索也会匹配标签名。填了「执行提示」的标签会在每次执行前以 `标签提示` 段注入到执行 Prompt 之前;没填的标签只作展示与筛选,因此无标签任务的 Prompt 与加入该功能前逐字节一致。标签在首次执行后仍可编辑:它们给任务分类、塑造下一次运行,而不是「已经跑过什么」的凭证。筛选行的**管理标签**入口会打开标签管理器:列出每个在用标签及其任务数,重命名会写到所有卡片上(与已有同名标签合并,并保留该标签的执行提示),删除会从全部任务(含已归档)上移除。两者都是 Host 的整账本事务,因此多个窗口看同一块看板时状态一致。
19
34
  - **子任务与级联执行**:任务可以有子任务——在详情页新建子任务(表单以父任务的工作区、模式、权限和模型为起点,每一项都可覆盖),或把一个还没有父任务的现有任务关联为子任务;子任务不在运行中时也可以解除关联。父子关系的准入由 Host 判定:父任务必须存在且在看板上、任务不能挂到自己的后代之下、树的深度不得超过配置的 `maxSubtaskDepth`(默认 1 层,最大 3 层)。执行一个任务会并发执行它整棵子任务树——每个成员各开一个 DSH 会话,同属一个 run group——父任务卡片会一直留在「进行中」列,直到它自己的回合与全部子任务都结算;任一成员失败则父任务判为失败,而单个子任务失败不会阻塞其它子任务。看板会标注父任务与子任务卡片,详情页列出直接子任务,顶栏提供一键隐藏子任务。
20
- - **Agent Team 执行(任务级 opt-in)**:任务可在详情页(或经 `task_board_create`/`task_board_update`)开启团队执行。执行这样的任务只启动一个会话——Team Lead——随后 Host 通过 Agent Teams 服务在它内部为每个子任务派生一个 teammate,并把 teammate 的会话挂到该子任务的 execution 上。整棵子树被压平成这一个 Team(只有 Lead 可以派生);Lead 的提示词按名字列出每个 teammate 并指向团队工具,团队执行由 Lead 判定统辖:teammate 自己已完成的回合即结算它的卡片(即使 Team 仍让该会话存活),Lead 自身结果落定后,仍未报结果的成员一并按该判定结算。普通级联的提示词结构相同,只是改为列出各独立会话。该模式依赖可选的 `agentTeams` 服务:未提供该服务的部署会明确拒绝执行而不是静默降级;子任务自己钉住的高于默认的权限也会被拒绝,因为 teammate 无法承载该钉住值。团队执行总是新建 Lead 会话(同一 Team 内 teammate 名字不可变),并使用 `teamProvider`。会话历史完全读不到的 execution 会在轮询满两分钟后带原因判为失败,因此读不到历史的会话不会把卡片永久留在运行列。
21
- - **项目分区(issue #1536)**:看板头部新增项目下拉,选项来自当前部署的 DSH 工作区——「全部项目」加每个已注册项目。选定项目后各列只显示钉在该项目上的任务,未钉工作区的任务只在「全部项目」下可见;在某个项目下打开「新建任务」会把该项目预选为任务工作区,「新建项目…」则用与 GUI「添加项目」相同的运行时接口把宿主目录注册为项目。
35
+ - **Agent Team 执行(任务级 opt-in)**:任务可在详情页(或经 `task_board_create`/`task_board_update`)开启团队执行。执行这样的任务只启动一个会话——Team Lead——随后 Host 通过 Agent Teams 服务在它内部为每个子任务派生一个 teammate,并把 teammate 的会话挂到该子任务的 execution 上。整棵子树被压平成这一个 Team(只有 Lead 可以派生);Lead 的提示词按名字列出每个 teammate 并指向团队工具,团队执行由 Lead 判定统辖:teammate 自己已完成的回合即结算它的卡片(即使 Team 仍让该会话存活),Lead 自身结果落定后,仍未报结果的成员一并按该判定结算。普通级联的提示词结构相同,只是改为列出各独立会话。该模式依赖可选的 `agentTeams` 服务:未提供该服务的部署会明确拒绝执行而不是静默降级;子任务自己的权限钉住若高于 Lead 会话实际权限也会被拒绝,因为 teammate 继承 Lead 会话的权限且无法被收窄;Lead 已覆盖的钉住则让该成员按 Lead 的权限照常执行,而不是让整次运行失败。团队执行总是新建 Lead 会话(同一 Team 内 teammate 名字不可变),并使用 `teamProvider`。会话历史完全读不到的 execution 会在轮询满两分钟后带原因判为失败,因此读不到历史的会话不会把卡片永久留在运行列。
36
+ - **项目分区(issue #1536)**:看板头部新增项目下拉,选项来自当前部署的 DSH 工作区——「全部项目」加每个已注册项目。选定项目后各列只显示钉在该项目上的任务,未钉工作区的任务只在「全部项目」下可见;在某个项目下打开「新建任务」会把该项目预选为任务工作区,「新建项目…」则用与 GUI「添加项目」相同的运行时接口把宿主目录注册为项目。未选项目而创建的根任务——无论来自表单还是看板的 agent 工具——继承创建它的会话所在的工作区;仍未钉住工作区的卡片在部署最近使用的工作区里执行,而不是落到宿主自身的工作目录。
22
37
  - **粘贴文本 AI 解析(issue #1540)**:新建任务表单可以把从别处复制的一段话交给模型,整理成标题、描述和执行 Prompt。模型取当前部署已配置的模型(与任务「模型」下拉同一份列表,默认选中第一项),调用在宿主侧完成,走看板既有的回环与同源校验,预算 45 秒并支持取消。解析结果只填进表单:不提交就不会创建任务,解析失败也不会改动你已经输入的内容。
23
38
  - **可折叠的任务表单**:新建/复制/子任务对话框把配置分成可折叠区域——任务内容默认展开,其后依次是标签、执行设置、运行方式、交接与续跑、定时运行——每个折叠区域都保留一行当前值摘要,因此对话框不需要滚动条(正文仍保留极矮窗口下的溢出兜底)。Agent 预设字段更名为「Agent 预设」,把部署内置预设以本地化名称列出,并附上任何用户预设;默认的「继承」选项会写明未钉住时真正使用的预设。
24
39
  - **Host 权威账本**:任务、计划和执行记录存于 `$DSH_HOME/task-board/ledger-v2.json`;浏览器动作只有经 Host 确认后才成为 UI 状态。
25
40
  - **有界执行历史**:每个任务只保留最近 20 条执行记录;新运行开始时截掉最旧的记录,使账本大小与每次写入成本不随任务历史无限增长。
26
41
  - **真实执行**:手动运行和定时运行共用 Host runner,默认新建独立会话、重命名、应用 agent 预设和 `/permission <id>`,再以 queue 模式发送任务 Prompt。
27
- - **目标驱动执行(默认开启)**:除非卡片在详情页取消勾选(新建任务对话框默认勾选),runner 会先入队任务 Prompt,再以同一份组合 Prompt 作为目标武装 dsh 内置的 `/goal`。dsh 的目标轮次驱动器会在同一会话中不断开启续跑轮次,直到 agent 标记目标完成;看板则让该执行一直保持 `running`,直到目标离开 active 阶段:目标正常完成判为成功,被阻塞则以目标自身的原因判为失败。该选项按任务设置(`goalRun`,缺席即开启),`task_board_create`/`task_board_update` 同样可用。
28
- - **可选会话复用**:任务可选择在上一执行会话中继续(issue #1419)。仅当该会话空闲且仍在运行时会话名册中才复用——Host 会重新应用钉住的权限与模型再以 queue 模式发送 Prompt,会话标题与历史保持不变;否则照旧新建会话,因此名册未知或会话正忙都不会阻塞定时运行。
42
+ - **目标驱动执行(卡片逐张选择加入,且需总开关开启)**:全局原生 `/goal` 开关(见下,默认关闭)是总开关;开启后,只有卡片在详情页勾选加入(新建任务对话框默认不勾选)时,runner 才会先入队任务 Prompt,再以同一份组合 Prompt 作为目标武装 dsh 内置的 `/goal`。dsh 的目标轮次驱动器会在同一会话中不断开启续跑轮次,直到 agent 标记目标完成;看板则让该执行一直保持 `running`,直到目标离开 active 阶段:目标正常完成判为成功,被阻塞则以目标自身的原因判为失败。该选项按任务设置(`goalRun`,缺席即关闭,因此从未动过该选项的卡片只执行一轮普通对话),`task_board_create`/`task_board_update` 同样可用。勾选框始终可操作:总开关只决定该偏好是否生效,从不决定它能否被表达,也绝不改写已保存的值。
43
+ - **任务验收(默认开启)**:以 goal 形式执行的任务必须先通过一次验收,`update_goal(action: complete)` 才会生效——看板在官方「工具执行前」生命周期上拦截这次调用,而不是相信 agent 的自述。验收沿用已安装 dsh-llm-verifier(MIT)的默认最终验收:三项 coding 判据(Specification Adherence、Output Match、Error Signal Detection)、总分与每项判据都必须达到 0.65 阈值、每项两轮并交换 A/B 位置后取平均、以任务真实轨迹(工具调用及其输出、assistant 文本)对比空工作基线,实际工作必须胜过该基线;部署记录工作区变更时,还会附上宿主自身对「本次运行改了磁盘上什么」的记录(文件清单与增删行数、有界的逐文件对比)作为提示词的参考上下文,使「只在自述里写过的补丁」无法通过。否决只有在**可定位**时才记为质量判定:任何导致不通过的判据都必须带有结构化问题反馈,写明它施加的任务要求、未达标的实际观测、差异,以及对任务、本次执行自己的轨迹或宿主工作区记录的逐字引用。没有反馈的否决、空泛或空的反馈、引用并不出现在所审证据中、引用空工作基线、以及「判据不通过却没有它自己的证据」,都记为**无效验收**:不消耗质量额度、不判卡片失败,并单独有界(两次);额度用尽后看板停止调用裁判、把卡片留给人工处理。若某次执行的记录只有无效验收,它同样因没有匹配的通过记录而结束,但使用独立终态原因,说明验收本身不可用,并明确否认这是有证据的质量判负。轨迹被截断导致裁判看不到输出时同样按无效验收处理,因为证据不足不等于工作没做。同一 execution 最多验收两次:第一次不通过把总分、逐项分数与可定位问题交回修复的 agent,第二次仍不通过即判该 execution 失败。执行**通过**并结算后,验收专用详细材料(问题反馈与结构化引用)会自动移除,只保留轻量审计凭据——判定、分数、时间、裁判路由、证据哈希与一行问题摘要。该 execution 记录之外的任何东西都不动;失败或无效的执行保留修复与复核所需的全部材料;移除是幂等的;清理失败会被记录并在宿主下次启动时重试,且绝不撤销通过记录。额度记录在 execution 记录上,因此重复完成调用、跨入下一 goal 轮次、插件重载与宿主重启都复用同一周期;重跑或一次定时触发则是新的 execution、重新计数。验收异常(超时、鉴权失败、裁判回答无法解析、裁判路由不可解析)与质量判定分开记录,不消耗质量额度。异常额度用尽并不把卡片判死:裁判路由答不上来是环境故障而不是工作不合格,此时执行周期保持打开、不再发起裁判调用,直到用户通过 `task_board_manage(action: reset-verification)` 清除已记录的异常。若一次验收在产生任何判定前就耗尽时间预算,会单独记为一类结果,不消耗任何额度,同样不判死卡片。两个上限都是配置项(`goalVerificationCallTimeoutSeconds`、`goalVerificationBudgetSeconds`),按需实时读取;剩余时间装不下一次调用时不会发起这次注定被中断的调用。运行列显示「执行中 / 验收中 / 验收未通过修复中」,每条 execution 记录展示判定、裁判模型与强度、次数、各项分数与阈值、问题反馈、证据范围与 token 用量。强制验收还堵住了旧回退路径:暂停的 goal、读取失败的 projection、已完成的回合与人工强制结算,都不能在没有匹配通过记录的情况下把 goal 执行结算为成功。带着验收记录失败意味着裁判给出了判定而工作没通过;一条验收记录都没有的失败则单独说明:验收门从未打开,因为执行会话没有调用 `update_goal(action= complete)`(常见于只在回复里宣告完成),交付从未被判定,更谈不上被判负。**单张卡片可以退出这道门**:新建任务与任务详情都有「跳过本任务的验收」勾选(默认不勾,语义与「同一对话继续」「以 /goal 开始执行任务」同级的卡片级选项),勾选后该卡以 goal 形式执行时不再拦截完成声明,执行记录标注 `applicability: skipped` 并在验收面板写明「该任务卡勾选了跳过验收」——它与总开关关闭是两回事,看板会分别说明。只对之后新开的执行生效,运行中改勾选不会改写已在飞的执行的判定规则。
44
+ - **可选会话复用**:任务可选择在上一执行会话中继续(issue #1419)。仅当该会话空闲且仍在运行时会话名册中才复用——Host 会重新应用钉住的权限与模型再以 queue 模式发送 Prompt,会话标题与历史保持不变;否则照旧新建会话,因此名册未知或会话正忙都不会阻塞定时运行。名册读取就发生在这次启动本身,所以长期空闲的看板会复用一个一直空闲的会话,而不是因为缓存过期而拒绝它。
45
+ - **空闲时停止会话轮询(issue #1821)**:只有当看板确实有需要名册裁决的事情时,Host 才读取 DSH 会话名册——存在待核对的执行,或运行列里有一张卡片(它的结果可能来自本进程没观测到的结算)。空看板、没有运行中卡片、没有未结算执行、也没有已启用计划时不轮询任何东西,因此空闲的 Host 不再按固定心跳重建每一条持久化会话行(字符串转换、对象分配、每条记录一次 stat)。有工作时按配置项 `sessionPollSeconds` 的节奏轮询;读取失败或名册仍未知时按翻倍延迟退避,上限一分钟,因此会话树故障时不会被按轮询间隔反复冲击。
29
46
  - **钉子失败即关闭**:工作区缺失、预设缺失或损坏、权限命令被拒绝时,任务 Prompt 不会发送。
30
- - **Host 调度器**:5 段 cron 支持 `*`、`*/n`、范围、逗号列表和周日 `0/7`。每条规则携带自己的 IANA 时区(默认跟随 Host 时区),日期/星期遵循 Vixie 语义:两个字段都受限时为 OR,其余组合为 AND。夏令时切换处的墙上时间空洞会被跳过,重复出现的歧义时刻只触发一次并取更早者。
47
+ - **Host 调度器**:5 段 cron 支持 `*`、`*/n`、范围、逗号列表和周日 `0/7`。每条规则携带自己的 IANA 时区(默认跟随 Host 时区),日期/星期遵循 Vixie 语义:两个字段都受限时为 OR,其余组合为 AND。夏令时切换处的墙上时间空洞会被跳过,重复出现的歧义时刻只触发一次并取更早者。规则有两种计划:循环 cron(可设次数上限,支持不限、1、2 或任意正整数)或在未来某一时刻只执行一次的单次计划;Host 只统计它真正开启的执行,达到上限或单次已执行的规则会在账本里自行停止,而不是等被执行的工作去关掉它。
31
48
  - **确定性恢复**:已有 session id 的 running execution 在重启后继续观察;没有 session id 的启动中断会取消且不会重发。
32
49
  - **实时同步**:变更返回完整 revision snapshot;SSE 只提示 revision、scheduler 与 power 变化,重连和页面恢复可见时重新拉完整 snapshot。
33
50
  - **可选空闲睡眠保护**:默认关闭;开启后覆盖全部运行中的 DSH 会话、已启用且未归档的任务计划和未知会话状态。
34
51
  - **系统提示词注入**:Host 通过 `SystemPrompt.section` 注册 order 200 的 `plugin:task-board` 段;任务看板设置可单独关闭声明而不关闭看板。该提示也会提醒 agent 在最终回复前收尾可见的 `todo_write` 计划列表。
35
- - **Agent 工具**:每个会话都可使用八个面向模型的工具(`task_board_list`、`task_board_get`、`task_board_create`、`task_board_update`、`task_board_set_parent`、`task_board_run`、`task_board_manage`、`task_board_schedule`),它们驱动与浏览器完全相同的 Host 账本,因此 agent 可以在对话里列出看板、创建子任务、关联或解除关联、执行级联、移动/归档/恢复/删除卡片、对看板已无法观察的卡片强制结算,以及为卡片配置 cron 计划。
52
+ - **Agent 工具**:每个会话都可使用八个面向模型的工具(`task_board_list`、`task_board_get`、`task_board_create`、`task_board_update`、`task_board_set_parent`、`task_board_run`、`task_board_manage`、`task_board_schedule`),它们驱动与浏览器完全相同的 Host 账本,因此 agent 可以在对话里列出看板、创建子任务、关联或解除关联、执行级联、把卡片移到任意列(含不经执行直接声明完成、失败或进行中)、归档/恢复/删除卡片、对看板已无法观察的卡片强制结算,以及为卡片配置 cron 计划。
53
+ - **Host 之外完成的执行结果(issue #1826)**:不由本 Host 执行的工作——例如在 DSH 之外用各自模型工作的 Codex、Claude Code——可以登记成一次真实执行,而不是只改一列。`task_board_manage(action: 'record-external-outcome', result: 'succeeded'|'failed', initiatedBy: <完成者>)` 会追加一条 execution 记录,带上判定、完成者与可选摘要;终态列由这条记录推导,与 Host 结算走同一条规则(`core/tasks.ts` 的 `settledStatus`)。该记录带 `external` 标记、永远不带 session,并在卡片仍有未结算 execution 时被拒绝(本 Host 正在执行时它才是唯一权威),归档卡片同样拒绝。`running` 与 `cancelled` 不能这样登记:它们只属于 Host。手工 `move-done`/`move-failed` 仍然可用,也仍然只写列——区别在于这一条会留下记录,因此列与执行历史永远不会脱节。
54
+
55
+ - **外部提供方扩展**:GitHub Issues 扩展(`@linxin666/dsh-client-ui-task-board-github`,默认开启)把 GitHub Issues 同步成看板卡片。它是本看板的提供方,因此其配置渲染在本插件自己的设置卡里——仅当扩展已安装时该区块才出现,且只有扩展开启时才显示仓库与凭据表单;见[扩展包 README](../dsh-task-board-github/README.zh.md)。
36
56
 
37
57
  ## 架构与协议
38
58
 
39
59
  看板视图在首次打开时渲染,关闭后重新打开会保留本地视图状态。Host 同步、调度和执行独立于视图持续运行。
40
60
 
41
61
  - `src/index.ts` 通过官方 `@deepseek-ai/dsh-api-gateway`、`@deepseek-ai/dsh-workspace` 与 `@deepseek-ai/dsh-host-webserver` SDK 挂载 Host 服务。
42
- - `src/host-ledger.ts` 串行动作,并用临时文件加原子 rename 持久化 `{ schemaVersion: 3, revision, tasks, scheduler, recentRequests }`。
43
- - `src/host-service.ts` 负责 cron 触发武装(按账本最近的到期时刻武装一次性定时器,宿主未提供 `timer` 服务时回退到进程定时器)、错过触发跳过、runner 启动、重启对账和电源保护理由。
62
+ - `src/host-ledger.ts` 串行动作,并用临时文件加原子 rename 持久化 `{ schemaVersion: 6, revision, tasks, scheduler, recentRequests }`。
63
+ - `src/host-service.ts` 负责调度触发武装(按账本最近的到期时刻武装单个定时器,宿主未提供 `timer` 服务时回退到进程定时器)、错过触发跳过、runner 启动、重启对账和电源保护理由;次数预算由账本自己执行,因此单次计划或已用尽上限的循环规则绝不会被第二次开启。
44
64
  - `src/client/native-panel.tsx` 注册看板的侧栏行与中栏页面(官方 `sidebar.panellist` 列表座位与 keyed `main` 座位),并维护与 ssh 面板的中栏互斥协议。
45
65
  - `src/client/host-api.ts` 单次导入旧浏览器数据、提交幂等动作,并把 Host snapshot 当作唯一已确认 UI 状态。
46
66
  - 同源接口为 `GET /api/task-board/state`、`GET /api/task-board/events` 和 `POST /api/task-board/action`。
@@ -56,8 +76,8 @@
56
76
  - `task_board_update`:修改内容、标签与执行目标;空字符串清除某个目标,空标签数组清除全部标签。
57
77
  - `task_board_set_parent`:把现有卡片挂到某个父任务下,或用空 parentId 解除关联。
58
78
  - `task_board_run`:立即执行一张卡片(可选择重跑已结算的卡片),并级联执行它整棵子任务树;会消耗真实 API 额度,且未确认的高于默认权限的绑定会以 `confirmation-required` 被拒绝。
59
- - `task_board_manage`:在待办与待规划之间移动卡片、归档或恢复、删除,或对看板已无法观察的运行中卡片强制结算(记为 cancelled 并记录调用者原因,卡片回到待办),同样受 Host 的运行中子任务与父子关系护栏约束。
60
- - `task_board_schedule`:启用、修改或关闭卡片的 cron 计划,包括其 IANA 时区(`timeZone` 传空字符串即清除,回退到 Host 时区)。
79
+ - `task_board_manage`:把卡片移到任意列——只写列、绝不伪造执行记录,因此待规划/待办用于规划、已完成/已失败用于声明工作在受追踪的执行之外结束、进行中用于表示工作正在进行但没有会话;真正有未结算执行的卡片会拒绝移动直到结算——归档或恢复、删除,或对看板已无法观察的运行中卡片强制结算(记为 cancelled 并记录调用者原因,卡片回到待办),同样受 Host 的运行中子任务与父子关系护栏约束。
80
+ - `task_board_schedule`:启用、修改或关闭卡片的计划——循环 cron(可选 `maxRuns` 上限,传 `0` 清空回不限次)或在 `at`(epoch 毫秒)指定时刻只执行一次的单次计划——包括其 IANA 时区(`timeZone` 传空字符串即清除,回退到 Host 时区)。
61
81
 
62
82
  这里刻意没有「确认权限」工具:该门禁存在的意义就是由人工放行高于默认值的权限,若 agent 能自行盖章,这道门就形同虚设。遇到 `confirmation-required` 的 agent 应当请用户在界面确认该卡片。工具调用带归属:执行会把发起会话记为 initiator,创建/更新则会把发起会话写进续接卡片快照。
63
83
 
@@ -81,6 +101,8 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
81
101
 
82
102
  ## 配置
83
103
 
104
+ 本插件的设置卡把选项组织成可折叠的分区——看板与运行行为、任务验收,以及提供方扩展(GitHub Issues 集成)贡献的分区。每个分区默认折叠,整张卡共用一次保存。
105
+
84
106
  | 键 | 默认值 | 行为 |
85
107
  | --- | --- | --- |
86
108
  | `enabled` | `true` | 启用 Host 服务与浏览器看板。 |
@@ -88,8 +110,15 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
88
110
  | `preventIdleSleep` | `false` | 存在运行中的 DSH 会话、已启用计划或未知会话状态时,持有一个系统空闲睡眠断言。 |
89
111
  | `trustedProxyHosts` | `[]` | 仅通过已认证 loopback 反向代理路径接受的规范 `host[:port]` authority 白名单。 |
90
112
  | `proxyTokenEnv` | `DSH_TASK_BOARD_PROXY_TOKEN` | 保存反向代理 token 的环境变量名;token 本身不会写入插件配置。 |
91
- | `sessionDefaultPermission` | `read-only` | 部署的会话默认权限。卡片有效权限(交接包或钉住字段)高于该值时,运行前必须经人工确认;cron 拒绝调度待确认卡片。 |
113
+ | `sessionDefaultPermission` | 跟随宿主 | 权限确认门的显式基线。未设置时跟随宿主自己的默认权限预设(DSH 新会话的起始权限),部署不提供权限目录时回退 `read-only`。卡片有效权限(交接包或钉住字段)高于该基线时,运行前必须经人工确认;cron 拒绝调度待确认卡片。 |
92
114
  | `maxSubtaskDepth` | `1` | 子任务深度上限,1 到 3。取 1 时一个任务只能有一层子任务,子任务不能再创建或关联子任务;每多一层,执行根任务一次开启的会话数就会成倍增加。 |
115
+ | `sessionPollSeconds` | `5` | 看板有需要核对的事情(运行中的卡片或未结算执行)时,Host 重新读取 DSH 会话名册的间隔秒数。两者都没有的空闲看板完全不轮询。取值 1 到 300;间隔越短结算越快、Host 负载越高。 |
116
+ | `goalRunEnabled` | `false` | 全局原生 `/goal` 执行开关。关闭时所有卡片都按普通单回合执行,看板不再武装 dsh 内置的 `/goal`;只有它开启、且卡片自身的「以 /goal 开始执行任务」被勾选时(该选项默认关闭),运行才使用原生 /goal。该值在执行启动时冻结,因此实时修改只影响之后新开的执行,卡片存储的 `goalRun` 偏好绝不被改写。这类运行记录为 `goal-disabled`:既不会被报告为已验收通过,也不会因缺少完成调用被判失败。与 `goalVerification` 相互独立。 |
117
+ | `goalVerification` | `true` | 本插件所开执行的 goal 验收。开启后,任务执行内的 `update_goal(action: complete)` 在该执行取得验收通过记录前一律被拒绝。只影响以 goal 形式执行的运行:普通聊天与未勾选 `goalRun` 的卡片不受影响。单张任务卡可以用「跳过本任务的验收」勾选退出本开关的约束(见下)。 |
118
+ | `goalVerificationModel` | `''`(继承) | 裁判模型路由,形如 `provider/model`。留空继承宿主模型目录的默认路由,绝不是任务卡钉住的执行模型。 |
119
+ | `goalVerificationReasoningEffort` | `''`(继承) | 裁判推理强度。留空继承宿主默认档位;目标模型未声明的档位绝不传参,回退到该模型自身默认档位并记录、展示。 |
120
+ | `goalVerificationCallTimeoutSeconds` | `150` | 单次裁判调用的上限(秒,范围 30–600)。一次验收要发六次裁判调用,上限过小会把「慢但健康」的路由变成一整排超时。 |
121
+ | `goalVerificationBudgetSeconds` | `1200` | 单次验收的总时间上限(秒,范围 120–1800),覆盖证据渲染与全部裁判调用(含重试)。应不小于单次调用上限的六倍,否则一次验收永远跑不完;耗尽时记为一类「预算耗尽」结果,不产生任何判定,也不消耗任何额度。 |
93
122
  | `teamProvider` | `spawn` | Agent Teams 服务用来组成 teammate 的 continuable-subagent provider。仅团队执行模式使用,与 Agent Teams 工具插件的 `freshProvider` 默认值一致。 |
94
123
 
95
124
  浏览器直接访问仍限制为 DSH loopback origin。若使用同机认证反向代理,应让 DSH Web 绑定 loopback,配置 `trustedProxyHosts`,在 `proxyTokenEnv` 指定的环境变量中放置高熵 token,并让代理在完成认证后替换(不能透传客户端提供的)`X-Dsh-Task-Board-Proxy-Token`。代理 Host 必须在白名单内,浏览器 `Origin` 必须与其 authority 相同。修改这些 composition 级代理设置后需重启 Host。
@@ -98,12 +127,14 @@ macOS 后端启动 `/usr/bin/caffeinate -i -w <host-pid>`,绝不请求 `-d`。
98
127
 
99
128
  ## 数据存储与迁移
100
129
 
101
- - 权威账本文件固定为 `$DSH_HOME/task-board/ledger-v2.json`(文件名为历史沿用);当前文档 schema 为 v3,v2 文档会在下一次 Host 启动时逐字段无损迁移为 v3 并原地写回。POSIX 新文件权限为 `0600`;Windows 继承用户目录 ACL。
130
+ - 权威账本文件固定为 `$DSH_HOME/task-board/ledger-v2.json`(文件名为历史沿用);当前文档 schema 为 v5,更旧的文档(v2/v3/v4)会在下一次 Host 启动时逐字段无损迁移并原地写回。POSIX 新文件权限为 `0600`;Windows 继承用户目录 ACL。
131
+ - v5 增加每 execution 的验收块(`verification`)。迁移刻意不补写该块:v5 之前开启的 execution 仍按历史判定结算,不会因为一个从未为它武装的完成门而被追溯验收。无法解析的验收块会被丢弃(该 execution 视为未验收,fail closed);import 带入的 execution 记录一律剥离该块,否则伪造的通过记录会让导入的工作看起来已验收。
102
132
  - v2 到 v3 迁移失败(任务行结构非法)时失败关闭并报出明确错误,原文件保持不动;绝不静默以空账本重启。损坏或未知 schema 的文件会移动到防碰撞的 `ledger-v2.json.corrupt-*` 名称,Host 以空账本和可见 scheduler 错误启动,不覆盖损坏字节。
103
133
  - 每个 origin 首次加载新版页面时,按稳定 source id 和 request id 导入 `dsh.taskBoard.v1`。任务按 id 合并,浏览器端严格较新的顶层字段优先,时间戳相同时保留 Host 字段,执行记录按 execution id 合并。
104
134
  - 最近 256 个 request id 与动作的 SHA-256 指纹会随账本持久化,因此 Host 重启后的变更重试仍保持幂等,且不会复制完整动作载荷。
135
+ - 外部结果 execution 记录只新增一个可选布尔字段(`external`)。该字段出现之前写入的账本没有它,读取时既不会凭空补写、也不会把「没有该字段」当成外部执行,因此旧文档原样读回;非布尔值会被丢弃,不会被当成可信来源标记。账本仍保持 v5:该字段在既有行里一律缺席,正是 v5 迁移已描述的那种增量变更。
105
136
  - 任务标签是任务行上的可选 `tags` 字段(`{ name, promptPrefix? }[]`),无需 bump schema:不含该字段的 v3 文档原样加载,非法的标签列表逐条修复(丢弃空名、重复项与超长名,并封顶数量),而不是丢掉整行任务。
106
- - /goal 选项是任务行上的可选 `goalRun` 字段,同样无需 bump schema:缺席即开启,显式 `false` 是退出选择,多写的 `true` 在加载时被修复回缺席。
137
+ - /goal 选项是任务行上的可选 `goalRun` 字段,同样无需 bump schema:缺席即关闭,显式 `true` 是加入选择,多写的 `false` 在加载时被修复回缺席。
107
138
  - 只有导入成功并经 Host 确认后,`dsh.taskBoard.v2.hostImported` 才保存当前 Host 账本 generation;新建或损坏恢复出的新 generation 会再次接收保留的 v1 数据。v1 localStorage 原值保持不变,作为只读回退备份。
108
139
  - 同一时间只有一个 Host 进程能通过 `$DSH_HOME/task-board/ledger-v2.lock` 持有任务看板账本目录;第二个使用同一 DSH home 的 Host 会失败关闭,不并发写账本。
109
140
 
@@ -137,7 +168,7 @@ pnpm --filter @linxin666/dsh-client-ui-task-board build
137
168
  2. 新建并编辑任务;刷新或打开第二个同源标签页,确认两者显示同一 Host revision。
138
169
  3. 执行一个钉住工作区、预设和权限的任务;确认出现新会话,并由该会话的 `turn/end` 历史结算任务。
139
170
  4. 在勾选 /goal 选项的情况下执行一个任务:确认会话启动了目标,卡片在多轮续跑期间保持 `running`,直到目标完成后结算为成功。取消勾选后再执行一次,确认变回单回合、由一条 `turn/end` 结算。
140
- 5. 启用一个即将到期的 cron,关闭全部浏览器页面,确认 Host 仍只创建并结算一次 execution。
171
+ 5. 启用一个即将到期的 cron,关闭全部浏览器页面,确认 Host 仍只创建并结算一次 execution。再把单次计划设在即将到来的时刻,确认恰好创建一次 execution 且规则显示为已结束;把循环规则的次数上限设为 2,确认第三次触发不再创建任何执行。
141
172
  6. 让 Host 停止并错过一个 cron 触发点,重启后确认该次被跳过,`nextRunAt` 从当前 Host 时间向后滚动。
142
173
  7. 开启 `preventIdleSleep` 并运行长任务,让显示器自动熄灭;恢复显示后确认会话继续且 execution 已结算。
143
174
  8. 关闭设置并禁用所有计划,再停止 DSH,确认 helper 退出;macOS 可用 `pmset -g assertions` 辅助确认插件没有 display-sleep assertion。
@@ -146,7 +177,8 @@ pnpm --filter @linxin666/dsh-client-ui-task-board build
146
177
  ## 已知限制
147
178
 
148
179
  - Host 停止、系统睡眠或长暂停期间错过的触发点会跳过,绝不排队补跑。
149
- - 同一任务已在运行时会跳过到期出现并滚动到下一 cron 匹配点;任务运行不并发、不排队。
180
+ - 同一任务已在运行时会跳过到期出现并滚动到下一 cron 匹配点;任务运行不并发、不排队。单次计划没有下一匹配点:因卡片正在运行或 Host 停机而错过的时刻会以「已跳过」结束该规则,绝不补跑。
181
+ - 有限次规则按调度器真正开启的执行计数,手动执行绝不消耗定时预算。任何会话存在之前就启动失败的那次会退回次数,规则因此保留下一次出现;而确实已到达会话的运行即使随后失败也计数。
150
182
  - DST 采用 Host 本地墙上时钟语义:春季跳时中不存在的分钟会跳过,秋季回拨中重复的分钟不会执行第二次。
151
183
  - 电源保护只阻止空闲系统睡眠,明确允许显示器睡眠与锁屏。
152
184
  - 合盖、手动睡眠、休眠、关机、低电量强制睡眠和企业电源策略不在保证范围内。
@@ -159,6 +191,13 @@ pnpm --filter @linxin666/dsh-client-ui-task-board build
159
191
  - 目标执行会按目标需要不断续跑:每一轮都消耗 API 额度,会话会一直忙碌,直到目标完成或被 dsh 阻塞(轮次上限、回合被拒或入队失败),后者在看板上记为执行失败。
160
192
  - 目标模式依赖运行时的 `/goal` 命令与命令派发器。两者都不提供的部署仍会把每张卡片当单回合执行,并在宿主日志中记录目标模式不可用,而不是让执行失败。
161
193
 
194
+ - 每次验收都会消耗模型请求:一次验收是 3 项判据 × 2 轮(6 次裁判请求),而同一 execution 最多验收两次,因此一个需要修复一次的 goal 周期最多在任务本身之外多花 12 次请求。
195
+ - 验收不估费用:本部署无法为目标裁判路由提供可靠的分 token 价格来源,报告只列出 token 用量。
196
+ - 宿主观察的工作区变更块只在部署确实记录变更且读取成功时出现;服务不可用或对比读取失败时,证据降级为只判轨迹,而不是让验收失败。
197
+ - 验收只在能生效的地方强制执行:没有成为 goal 执行的运行(`/goal` 被拒绝或不可用,或全局总开关关闭)与团队执行的 teammate 成员不受门禁,记录会明确说明,而不是暗示它们已被验收。总开关关闭的运行记为 `goal-disabled`(独立原因,与「命令被拒」区分),因此也不会被读成已验收通过。宿主不提供模型目录时无法解析裁判路由,完成声明按 fail closed 拒绝并记为验收异常;异常额度用尽后卡片保持打开而不是判死,恢复需要用户清除这些异常。
198
+ - 第一版一律使用 coding 判据:没有自动 rubric 选择,非工程任务也按工程判据评判(设置界面已说明)。
199
+ - 若第三方验收插件(例如已安装的 `dsh-llm-verifier`)也开启了自己的自动验收,两个裁判会各自对同一会话评分:本看板的验收决定能否完成,另一个只做提示,公开 SDK 接口无法让二者共享同一判定。要避免为两次判定付费,只能关闭对方的自动模式。
200
+
162
201
  ## 数据遥测
163
202
 
164
203
  浏览器半区每个 UTC 日向 dsh-market.com 发送一次匿名安装心跳:仅含一个 localStorage 随机 ID 与本包名,无其他数据。服务端只存储该 ID 的加盐哈希,不存 IP,且只暴露聚合计数。完整契约见 [docs/telemetry.md](../../docs/telemetry.md)。