@william2000/dsh-nova-ui-task-board 0.1.0

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 (50) hide show
  1. package/README.md +86 -0
  2. package/cordis.patch.yml +13 -0
  3. package/lib/client.js +5641 -0
  4. package/lib/client.js.map +1 -0
  5. package/lib/index.js +4873 -0
  6. package/lib/types/client/NovaTaskBoardSettingsCard.d.ts +17 -0
  7. package/lib/types/client/PlaceholderBoard.d.ts +9 -0
  8. package/lib/types/client/apply-guard.d.ts +25 -0
  9. package/lib/types/client/board/ConfirmDialog.d.ts +12 -0
  10. package/lib/types/client/board/ConfirmTaskDialog.d.ts +8 -0
  11. package/lib/types/client/board/ConvertTaskModal.d.ts +17 -0
  12. package/lib/types/client/board/NewTaskModal.d.ts +6 -0
  13. package/lib/types/client/board/RequirementSplitModal.d.ts +14 -0
  14. package/lib/types/client/board/SplitConfirmDialog.d.ts +9 -0
  15. package/lib/types/client/board/TagEditor.d.ts +6 -0
  16. package/lib/types/client/board/TaskBoard.d.ts +9 -0
  17. package/lib/types/client/board/TaskCard.d.ts +52 -0
  18. package/lib/types/client/board/TaskDetail.d.ts +7 -0
  19. package/lib/types/client/board/status-key.d.ts +8 -0
  20. package/lib/types/client/board-controller.d.ts +274 -0
  21. package/lib/types/client/board-mount.d.ts +10 -0
  22. package/lib/types/client/chat-integration.d.ts +99 -0
  23. package/lib/types/client/drag-utils.d.ts +78 -0
  24. package/lib/types/client/filter.d.ts +42 -0
  25. package/lib/types/client/grouping.d.ts +54 -0
  26. package/lib/types/client/host-api.d.ts +62 -0
  27. package/lib/types/client/index.d.ts +46 -0
  28. package/lib/types/client/legacy-store.d.ts +28 -0
  29. package/lib/types/client/locales.d.ts +202 -0
  30. package/lib/types/client/schedule-presets.d.ts +20 -0
  31. package/lib/types/client/sidebar-entry-core.d.ts +54 -0
  32. package/lib/types/client/sidebar-entry.d.ts +19 -0
  33. package/lib/types/core/context.d.ts +139 -0
  34. package/lib/types/core/cron.d.ts +54 -0
  35. package/lib/types/core/migrate.d.ts +39 -0
  36. package/lib/types/core/model.d.ts +446 -0
  37. package/lib/types/core/recovery.d.ts +25 -0
  38. package/lib/types/core/split.d.ts +81 -0
  39. package/lib/types/core/transitions.d.ts +436 -0
  40. package/lib/types/dsh-home.d.ts +21 -0
  41. package/lib/types/host-automation.d.ts +59 -0
  42. package/lib/types/host-ledger.d.ts +146 -0
  43. package/lib/types/host-routes.d.ts +65 -0
  44. package/lib/types/host-runner.d.ts +177 -0
  45. package/lib/types/host-service.d.ts +291 -0
  46. package/lib/types/index.d.ts +90 -0
  47. package/lib/types/loopback.d.ts +23 -0
  48. package/lib/types/mount-once.d.ts +9 -0
  49. package/lib/types/protocol.d.ts +210 -0
  50. package/package.json +89 -0
@@ -0,0 +1,17 @@
1
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
2
+ /** The task-board fields this card edits (the namespace's full schema). */
3
+ export interface NovaTaskBoardSettings {
4
+ /** Master switch for the plugin. */
5
+ enabled?: boolean;
6
+ /** Whether the board announces itself in every agent's system prompt. */
7
+ announceToAgent?: boolean;
8
+ /** Prevent host idle sleep while sessions run or schedules are armed. */
9
+ preventIdleSleep?: boolean;
10
+ }
11
+ /**
12
+ * The settings card component, bound to the `nova-task-board` scope.
13
+ * @param props - the bound scope for the card's namespace.
14
+ */
15
+ export declare function NovaTaskBoardSettingsCard(props: {
16
+ scope: SettingsScope<NovaTaskBoardSettings>;
17
+ }): JSX.Element | null;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * T001 占位看板视图:脚手架阶段只渲染标题与占位说明;业务看板 UI
3
+ * (五列、卡片、详情、新建、归档等)由 T004 实现。
4
+ */
5
+ import type { PlaceholderBoardController } from './board-controller.ts';
6
+ /** Placeholder board body for the scaffold milestone. */
7
+ export declare function PlaceholderBoard(_props: {
8
+ controller: PlaceholderBoardController;
9
+ }): JSX.Element;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Cross-module-instance apply guard for the nova task-board client bundle.
3
+ *
4
+ * The client factory can run more than once in a single page lifetime (for
5
+ * example when a stale bundle is mixed with a rebuilt one while `dsh web` is
6
+ * restarted). Without a guard, every factory run mounts its own sidebar entry
7
+ * and board view, so the shell ends up showing two "task board" rows.
8
+ *
9
+ * The flag lives on globalThis so separate module instances (independent
10
+ * factory runs) still share one guard. First claim wins; later claims become
11
+ * no-ops until the claim is released (fiber unload / hot-reload) or the page
12
+ * reloads.
13
+ */
14
+ declare global {
15
+ var __dshNovaTaskBoardApplied: boolean | undefined;
16
+ }
17
+ /** Claims the plugin apply slot. Returns true when this call won the slot. */
18
+ export declare function claimNovaTaskBoardApply(): boolean;
19
+ /**
20
+ * Releases the claim. Called from the client fiber cleanup so that a
21
+ * hot-reloaded bundle (the loader unloads the old plugin fiber and invokes
22
+ * the rebuilt one in the same page) can claim again instead of being
23
+ * silently dropped.
24
+ */
25
+ export declare function releaseNovaTaskBoardApply(): void;
@@ -0,0 +1,12 @@
1
+ /** Confirm overlay props. */
2
+ export interface ConfirmDialogProps {
3
+ title: string;
4
+ message: string;
5
+ confirmLabel: string;
6
+ /** Render the confirm button in the danger style. */
7
+ danger?: boolean;
8
+ onCancel: () => void;
9
+ onConfirm: () => void;
10
+ }
11
+ /** Small confirm overlay. */
12
+ export declare function ConfirmDialog({ title, message, confirmLabel, danger, onCancel, onConfirm }: ConfirmDialogProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import type { TaskRecord } from '../../core/model.ts';
2
+ import type { BoardController } from '../board-controller.ts';
3
+ /** 确认对话框浮层。 */
4
+ export declare function ConfirmTaskDialog({ controller, task, onClose }: {
5
+ controller: BoardController;
6
+ task: TaskRecord;
7
+ onClose: () => void;
8
+ }): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import type { BoardController } from '../board-controller.ts';
2
+ /** 转任务表单的预填内容(由 chat-integration 从消息提取)。 */
3
+ export interface ConvertTaskPrefill {
4
+ title: string;
5
+ description: string;
6
+ prompt: string;
7
+ /** 来源会话 id(propose 时写入 metadata,可跳回对话)。 */
8
+ sourceConversationId?: string;
9
+ /** 来源消息 id(propose 时写入 metadata)。 */
10
+ sourceMessageId?: string;
11
+ }
12
+ /** 转为任务表单浮层。 */
13
+ export declare function ConvertTaskModal({ controller, prefill, onClose }: {
14
+ controller: BoardController;
15
+ prefill: ConvertTaskPrefill;
16
+ onClose: () => void;
17
+ }): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import type { BoardController } from '../board-controller.ts';
2
+ /** 新建任务表单浮层。 */
3
+ export declare function NewTaskModal({ controller, onClose }: {
4
+ controller: BoardController;
5
+ onClose: () => void;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { BoardController } from '../board-controller.ts';
2
+ /** 递归拆分的预填(从任务详情发起;§12.9「对任一任务可再次发起拆分」)。 */
3
+ export interface RequirementSplitPrefill {
4
+ /** 需求标题(父任务「需求:<标题>」)。 */
5
+ title?: string;
6
+ /** 递归拆分:新父需求任务挂到该任务下。 */
7
+ parentTaskId?: string;
8
+ }
9
+ /** 需求拆分对话框浮层。 */
10
+ export declare function RequirementSplitModal({ controller, prefill, onClose }: {
11
+ controller: BoardController;
12
+ prefill?: RequirementSplitPrefill;
13
+ onClose: () => void;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import type { TaskRecord } from '../../core/model.ts';
2
+ import type { BoardController } from '../board-controller.ts';
3
+ /** 批量确认对话框浮层。 */
4
+ export declare function SplitConfirmDialog({ controller, tasks, onClose }: {
5
+ controller: BoardController;
6
+ /** 待确认列中的候选任务(含父需求任务;快照传入,行编辑不影响账本)。 */
7
+ tasks: readonly TaskRecord[];
8
+ onClose: () => void;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ export declare function TagEditor({ tags, suggestions, disabled, onChange }: {
2
+ tags: readonly string[];
3
+ suggestions: readonly string[];
4
+ disabled: boolean;
5
+ onChange: (tags: string[]) => void;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { type BoardController } from '../board-controller.ts';
2
+ /**
3
+ * 看板组件;订阅控制器快照渲染。拖拽状态(drag/hover/pending/dragError)为
4
+ * 组件内会话级状态:drag = 拖拽来源、hover = 当前落点(指示条)、pending =
5
+ * 提交在途的本地占位渲染(Host 确认后清除)、dragError = 非法落点提示。
6
+ */
7
+ export declare function TaskBoard({ controller }: {
8
+ controller: BoardController;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,52 @@
1
+ import type { DragEvent } from 'react';
2
+ import type { ExecutionRecord, ScheduleRule, TaskRecord, TaskSource } from '../../core/model.ts';
3
+ import { type NovaTaskBoardKey } from '../locales.ts';
4
+ /** 任务来源 → 文案键;无专用文案返回 undefined(卡片不渲染来源标记)。 */
5
+ export declare function sourceKey(source: TaskSource): NovaTaskBoardKey | undefined;
6
+ /** 绝对时间标签(Host 时区)。 */
7
+ export declare function formatHostTimestamp(ms: number, timeZone?: string): string;
8
+ /** 相对时间(<1m 刚刚;<1h 分钟;<24h 小时;更早回退到 Host 时区日期)。 */
9
+ export declare function formatTime(ms: number, timeZone?: string): string;
10
+ /** 执行结果标签(详情/卡片共用)。 */
11
+ export declare function executionLabel(execution: ExecutionRecord): string;
12
+ /** 定时徽标的数据驱动占位(T006/T007 启用编辑能力,展示自 T004 起)。 */
13
+ export declare function scheduleBadge(schedule: ScheduleRule | undefined): {
14
+ label: string;
15
+ at?: number;
16
+ } | undefined;
17
+ /** 卡片标签 chips 的最大展示数(§12.8:默认最多 3 个,溢出显示 +N)。 */
18
+ export declare const CARD_TAG_CHIP_LIMIT = 3;
19
+ /**
20
+ * 列内卡片(memo 化内层)。T009:可拖拽卡片上报 dragstart/dragover/drop/dragend。
21
+ * T011:proposed 候选卡片展示「待确认」角标 + 来源标记(§12.2/§14.4)。
22
+ * T012:确认后的子任务(parentId 存在)展示所属需求(父任务)徽标,可跳转
23
+ * (§12.9/§12.3 父子互跳)。徽标用 span 而非 button——卡片根元素是 button,
24
+ * 避免嵌套交互元素(点击经 stopPropagation 转发 onOpenParent)。
25
+ */
26
+ declare function TaskCardInner({ task, pending, timeZone, onClick, draggable, dragging, onDragStart, onDragEnd, onDragOverCard, onDropCard, parentId, parentTitle, onOpenParent }: {
27
+ task: TaskRecord;
28
+ pending: boolean;
29
+ timeZone?: string;
30
+ onClick: () => void;
31
+ /** 是否可拖拽(T009:非归档、非 running/proposed;归档视图不参与拖拽)。 */
32
+ draggable: boolean;
33
+ /** 是否正处于拖拽中(视觉高亮来源卡片)。 */
34
+ dragging: boolean;
35
+ /** 拖动启动(看板记录拖拽来源;卡片不直接提交任何动作)。 */
36
+ onDragStart: (task: TaskRecord) => void;
37
+ /** 拖动结束(看板清理拖拽状态)。 */
38
+ onDragEnd: () => void;
39
+ /** 悬停落点(卡片内按鼠标 Y 位置判定 before/after)。 */
40
+ onDragOverCard: (task: TaskRecord, event: DragEvent<HTMLElement>) => void;
41
+ /** 松手落点(看板据此提交 move/reorder;卡片不直接提交任何动作)。 */
42
+ onDropCard: (task: TaskRecord, event: DragEvent<HTMLElement>) => void;
43
+ /** 父需求任务 id(T012;确认后的子任务展示父徽标)。 */
44
+ parentId?: string;
45
+ /** 父需求任务标题(徽标文案;父任务缺失时展示占位文案)。 */
46
+ parentTitle?: string;
47
+ /** 父徽标点击:跳转父任务详情(stopPropagation,不触卡片的 open)。 */
48
+ onOpenParent?: (id: string) => void;
49
+ }): import("react").JSX.Element;
50
+ /** Memoized card: re-renders only when the card's own props change. */
51
+ export declare const TaskCard: import("react").MemoExoticComponent<typeof TaskCardInner>;
52
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { TaskRecord } from '../../core/model.ts';
2
+ import type { BoardController } from '../board-controller.ts';
3
+ /** 任务详情浮层。 */
4
+ export declare function TaskDetail({ controller, task }: {
5
+ controller: BoardController;
6
+ task: TaskRecord;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 任务状态 → 文案键(列标题与详情徽标)。
3
+ * proposed(待确认)属 T011;本表为其预留键,T004 的五列不渲染该列。
4
+ */
5
+ import type { TaskStatus } from '../../core/model.ts';
6
+ import type { NovaTaskBoardKey } from '../locales.ts';
7
+ /** Task status → locale key. */
8
+ export declare const STATUS_KEY: Record<TaskStatus, NovaTaskBoardKey>;
@@ -0,0 +1,274 @@
1
+ /**
2
+ * 看板控制器(T004):Host 账本的浏览器投影 + 视图状态 + 实时同步。
3
+ *
4
+ * 设计要点(DSH-REQ-001 §4-7/§8/§17):
5
+ * - **浏览器 = 异步视图,Host = 唯一事实源**:所有变更经 `{requestId, action}`
6
+ * 信封提交(T003 协议),UI 只认 Host 确认后的快照;`pendingTaskIds` 只做
7
+ * 展示(spinner/禁用),不先写未确认状态(§4-7/§17 一致性);
8
+ * - **实时同步(§11.1)**:SSE 帧 `{revision, scheduler, power}`——revision
9
+ * 与当前一致时仅就地更新 scheduler/power(不触碰任务列表),否则重拉全量
10
+ * state;页面重新可见/断线重连(传输层 onopen)同样触发重拉;
11
+ * - **按任务 memo 化的前提**:`acceptRemote` 对快照做「身份保持合并」——同账本
12
+ * 代数(ledgerId)+ 非倒退 revision 下,JSON 未变化的任务行保留对象引用,
13
+ * 兄弟卡片状态变化只替换变化的那张,memo 边界(TaskCard)因此真正生效;
14
+ * - **会话导航**:打开看板时基线化当前会话,会话列表 current 变化即自动关闭
15
+ * 看板(§12.1:点击会话/工作区/搜索结果行回到会话);详情页「查看会话」经
16
+ * sessions.open 跳转 transcript;
17
+ * - **执行选项**:工作区列表与 agent 预设来自运行时(非账本),控制器只转发;
18
+ * - **能力门**:`executionCapable`(run/rerun 是否已接线)。T004 基线为 false
19
+ * (协议 run/rerun 归 T005),详情页据此禁用执行按钮并提示;T005 起恒 true
20
+ * (协议已并入 run/rerun,Host 端 runner 恒接线)。
21
+ * - **排序/分组视图(T009 §12.10)**:`groupMode` 视图状态(手动顺序/按项目分组/
22
+ * 按标签分组,会话级记忆、刷新重置,不落账);`reorderTask` 提交列内排序/分组
23
+ * 落位(status 一致性 + order 重算由 Host 守卫)、`moveTask` 扩展可选 order
24
+ * (跨列拖拽落位,缺省追加目标列末尾)。拖拽采用「本地 pending 渲染 + Host 确认
25
+ * 落定、失败回滚」——控制器仍不先写未确认状态,pending 位置由 UI 层以快照为基
26
+ * 础做展示(§17 一致性:以 Host 确认的 order 为准)。
27
+ * - **对话候选流转(T011 §11.2/§14.4,P3.1)**:`proposeTask`(对话提取/手动转
28
+ * 任务 → proposed 候选,输入带来源会话/消息引用)、`confirmTask`(确认候选 →
29
+ * 落 backlog/todo,confirm 是唯一 promote 路径)、`dismissTask`(拒绝/忽略删除);
30
+ * `proposedCountOf` 提供入口未确认角标计数(§12.7)。
31
+ * - **需求拆分(T012 §11.2/§12.9/§14.5,P3.2)**:`startRequirementSplit`(提交
32
+ * 需求文本/文件路径 → Host 启动独立拆分会话,结算后父需求任务 + 批量候选进
33
+ * 「待确认」列,source=requirement、children parentId 指向父任务、递归拆分挂
34
+ * 到原任务下);批量确认/拒绝复用 `confirmTask`/`dismissTask`(T011 闸门)。
35
+ *
36
+ * 框架无关(结构面注入 transport/sessions/时钟/uuid),整段编排可脱离宿主单测。
37
+ */
38
+ import type { NewTaskInput, ProposeTaskInput, SchedulerSnapshot, SplitRequestInput, TaskRecord, TaskStatus, TaskUpdatePatch } from '../core/model.ts';
39
+ import type { GroupMode } from './grouping.ts';
40
+ import type { TaskBoardAction, TaskBoardEventPayload, TaskBoardPowerSnapshot, TaskBoardSnapshot } from '../protocol.ts';
41
+ /** 浏览器 ↔ Host 传输面(见 host-api.ts 的 HttpTaskBoardHostTransport)。 */
42
+ export interface TaskBoardTransport {
43
+ bootstrap(legacy: readonly TaskRecord[]): Promise<TaskBoardSnapshot>;
44
+ state(): Promise<TaskBoardSnapshot>;
45
+ action(action: TaskBoardAction): Promise<TaskBoardSnapshot>;
46
+ subscribe(listener: (event?: TaskBoardEventPayload) => void): () => void;
47
+ }
48
+ /** 会话列表的读取面(结构面:只读 current + 订阅)。 */
49
+ export interface SessionsListFace {
50
+ getSnapshot(): {
51
+ current: string | undefined;
52
+ };
53
+ subscribe(listener: () => void): () => void;
54
+ }
55
+ /** 会话导航面:打开一个会话即切回对话视图(同时关闭看板)。 */
56
+ export interface SessionsFace {
57
+ list: SessionsListFace;
58
+ open(id: string): void;
59
+ }
60
+ /** 工作区下拉选项(运行时事实)。 */
61
+ export interface ExecutionWorkspaceOption {
62
+ workspaceId: string;
63
+ /** 展示名(工作区标题;无标题时回退路径)。 */
64
+ title: string;
65
+ }
66
+ /** agent 预设下拉选项(运行时事实)。 */
67
+ export interface ExecutionPresetOption {
68
+ id: string;
69
+ name?: string;
70
+ description?: string;
71
+ /** 该预设为何不能组装会话(下拉禁用并展示)。 */
72
+ broken?: string;
73
+ isDefault: boolean;
74
+ }
75
+ /** 执行目标三件套(工作区/模式/权限)的选项集。 */
76
+ export interface ExecutionOptionsSnapshot {
77
+ workspaces: readonly ExecutionWorkspaceOption[];
78
+ presets: readonly ExecutionPresetOption[];
79
+ }
80
+ /** 控制器对外快照(UI 只读;订阅后整体替换)。 */
81
+ export interface BoardSnapshot {
82
+ tasks: readonly TaskRecord[];
83
+ boardOpen: boolean;
84
+ /** true = 归档视图(单列),false = 五列主看板。 */
85
+ archiveView: boolean;
86
+ /** 列内排序视图模式(T009 §12.10):手动顺序/按项目分组/按标签分组;会话级记忆、刷新重置。 */
87
+ groupMode: GroupMode;
88
+ selectedTaskId?: string;
89
+ executionOptions: ExecutionOptionsSnapshot;
90
+ /** 已提交、等待 Host 确认的任务 id(只做展示,不先写状态)。 */
91
+ pendingTaskIds: readonly string[];
92
+ transportError?: string;
93
+ /** Host 元信息:revision + scheduler(时区)+ power。 */
94
+ host?: {
95
+ revision: number;
96
+ scheduler: SchedulerSnapshot;
97
+ power: TaskBoardPowerSnapshot;
98
+ };
99
+ /** run/rerun 是否已接线(T005 前恒 false;详情页据此禁用执行按钮)。 */
100
+ executionCapable: boolean;
101
+ }
102
+ /** 当前选中的任务(按 selectedTaskId 从账本解析),无则 undefined。 */
103
+ export declare function selectedTaskOf(snapshot: BoardSnapshot): TaskRecord | undefined;
104
+ /** 未确认候选任务数(T011 §12.7 入口角标:proposed 且未归档)。 */
105
+ export declare function proposedCountOf(snapshot: BoardSnapshot): number;
106
+ /** 控制器依赖(全部结构面,测试可注入 fake)。 */
107
+ export interface BoardControllerDeps {
108
+ transport: TaskBoardTransport;
109
+ sessions: SessionsFace;
110
+ /** 时钟(测试注入)。 */
111
+ now?: () => number;
112
+ /** id 铸造(测试注入)。 */
113
+ uuid?: () => string;
114
+ /**
115
+ * v1 只读回退数据的惰性读取(§12 范围项 8):bootstrap 时传入 transport 做
116
+ * 一次性迁移。缺省不读(测试/无 v1 环境)。生产接线读 localStorage。
117
+ */
118
+ legacy?: () => readonly TaskRecord[];
119
+ }
120
+ /**
121
+ * 看板控制器(见模块注释)。所有变更 bump 快照并通知订阅者;UI 与 DOM 挂载
122
+ * 通过订阅重渲染。生产路径恒为 Host 背书(transport 必传)。
123
+ */
124
+ export declare class BoardController {
125
+ private readonly deps;
126
+ private tasks;
127
+ private boardOpen;
128
+ private archiveView;
129
+ private groupMode;
130
+ private selectedTaskId;
131
+ private executionOptions;
132
+ private readonly listeners;
133
+ private readonly disposers;
134
+ private readonly now;
135
+ private readonly uuid;
136
+ private readonly pendingTaskIds;
137
+ private readonly taskQueues;
138
+ private transportError;
139
+ private hostState;
140
+ private remoteSubscribed;
141
+ private remoteInitialization;
142
+ private started;
143
+ private lastCurrent;
144
+ /** @param deps - 传输、会话导航面、时钟与 id 铸造。 */
145
+ constructor(deps: BoardControllerDeps);
146
+ /** 启动:初始化 Host 同步 + 订阅 SSE + 会话导航。幂等。 */
147
+ start(): void;
148
+ /** 停止全部订阅并释放状态(幂等)。 */
149
+ dispose(): void;
150
+ getSnapshot(): BoardSnapshot;
151
+ subscribe(listener: () => void): () => void;
152
+ /** 生产变更是否由 Host 传输确认(T004 恒 true;保留语义供测试/回退判断)。 */
153
+ isHostBacked(): boolean;
154
+ /** 显式重试与 Host 的首次/全量同步(错误条「重试」按钮)。 */
155
+ retryHostSync(): Promise<boolean>;
156
+ openBoard(): void;
157
+ closeBoard(): void;
158
+ toggleBoard(): void;
159
+ /**
160
+ * 切换主看板/归档视图。离开归档视图时若选中任务仍是归档任务则清除选中
161
+ * ——详情不得悬停在已离开看板的任务上。
162
+ */
163
+ toggleArchiveView(): void;
164
+ /**
165
+ * 切换列内排序视图模式(T009 §12.10):手动顺序/按项目分组/按标签分组。
166
+ * 分组是视图层能力,不改底层 order 与归属数据;模式仅会话级记忆(刷新重置)。
167
+ */
168
+ setGroupMode(mode: GroupMode): void;
169
+ openTask(id: string): void;
170
+ closeTask(): void;
171
+ /**
172
+ * 经 Host 创建任务;**只有 Host 确认后**才在账本可见并返回任务(NewTaskModal
173
+ * 据此决定关闭/重试)。失败返回 undefined 并置 transportError。
174
+ */
175
+ createTaskConfirmed(input: NewTaskInput): Promise<TaskRecord | undefined>;
176
+ /**
177
+ * propose(T011 §11.2/§14.4):对话提取/手动转任务 → 创建 proposed 候选任务。
178
+ * 输入沿用 NewTaskInput + sourceConversationId/sourceMessageId(来源引用写入
179
+ * metadata,可跳回对话);清洗与命令字段拒绝在 Host(transitions/protocol)。
180
+ * 只有 Host 确认后才在账本可见并返回候选;失败返回 undefined 并置 transportError。
181
+ */
182
+ proposeTask(input: ProposeTaskInput): Promise<TaskRecord | undefined>;
183
+ /**
184
+ * confirm(T011 §10.2/§11.2):确认候选任务 → 落所选列(backlog/todo)。
185
+ * confirm 是候选进入正式看板的唯一路径(防注入闸门)。返回 Host 是否确认。
186
+ */
187
+ confirmTask(id: string, target: 'backlog' | 'todo'): Promise<boolean>;
188
+ /** dismiss(T011 §10.2/§11.2):拒绝/忽略候选任务(删除)。 */
189
+ dismissTask(id: string): void;
190
+ /**
191
+ * start-split(T012 §11.2/§12.9/§14.5):需求文档自动拆分。提交需求(标题 +
192
+ * 粘贴文本或工作区文件路径)后,Host 启动独立拆分会话驱动 agent 按 §14.5
193
+ * 方法论拆分;结算后父需求任务 + 批量候选进入「待确认」列(source=requirement,
194
+ * 人工确认闸门复用 T011——confirm 仍是唯一 promote 路径)。返回 Host 是否确认
195
+ * 受理(受理后拆分异步进行,SSE revision 变化时批量候选到达待确认列)。
196
+ */
197
+ startRequirementSplit(input: SplitRequestInput): Promise<boolean>;
198
+ updateTask(id: string, patch: TaskUpdatePatch): void;
199
+ /**
200
+ * 跨列移动(§10.2/§11.2 + T009 order 扩展):backlog/todo 互移、done/failed
201
+ * 拖回 backlog/todo 为重开(仅改状态不触发执行,区别于 rerun)。`order` 可选
202
+ * = 目标列落位下标(0-based,缺省追加目标列末尾);同列拖拽排序请用
203
+ * `reorderTask`。返回 Host 是否确认。
204
+ */
205
+ moveTask(id: string, status: TaskStatus, order?: number): Promise<boolean>;
206
+ /**
207
+ * 列内排序/分组落位(T009 §11.2 reorder):调整列内 order(`status` 须与任务
208
+ * 当前状态一致,Host 防竞态校验);`order` = 0-based 落位下标(Host 重算同列
209
+ * 唯一)。分组视图跨组落位可携带归属变更 `ownership`(`project` 空串清除 /
210
+ * `tags` 整体替换),与 order 原子落账。返回 Host 是否确认。
211
+ */
212
+ reorderTask(id: string, status: TaskStatus, order: number, ownership?: {
213
+ project?: string;
214
+ tags?: string[];
215
+ }): Promise<boolean>;
216
+ deleteTask(id: string): void;
217
+ /** 归档已结算任务(done/failed)。Host 守卫拒绝非法归档并置错误条。 */
218
+ archiveTask(id: string): void;
219
+ /** 恢复归档任务回原列。确认后关闭详情(任务已离开归档视图)。 */
220
+ restoreTask(id: string): void;
221
+ /**
222
+ * 执行任务(T005 §13.2):协议 `run` 开执行记录,Host 端 runner 异步创建
223
+ * 会话并发送 Prompt;本方法只提交,结算由 Host 轮询完成。
224
+ */
225
+ runTask(id: string): Promise<boolean>;
226
+ /** 重新执行(§13.2):协议 `rerun`,Host 侧先落回 todo 再开启执行。 */
227
+ rerunTask(id: string): Promise<void>;
228
+ /**
229
+ * 设定/修改/关闭任务的 cron 周期定时(T006 §11.2 set-schedule)。patch 只含
230
+ * enabled/cron;nextRunAt/lastTriggeredAt 由 Host 调度器维护(浏览器不可写,
231
+ * §9.4)。Host 拒绝(非法 cron / 归档 / proposed 等)→ transportError,账本不变。
232
+ */
233
+ setSchedule(id: string, patch: {
234
+ enabled?: boolean;
235
+ cron?: string;
236
+ }): void;
237
+ /**
238
+ * 设定/修改/清除任务的一次性计划(T007 §11.2 set-one-shot)。patch 只含
239
+ * runAt(缺省或 0 = 取消);firedAt 由 Host 调度器维护(浏览器不可写,
240
+ * §9.4)。设定自动清除 cron 分支、反之亦然(互斥 D6)。Host 拒绝(非有限
241
+ * runAt / 归档 / proposed 等)→ transportError,账本不变。
242
+ */
243
+ setOneShot(id: string, patch: {
244
+ runAt?: number;
245
+ }): void;
246
+ /** 替换执行选项集的一部分(工作区列表/预设名册来自运行时,非账本)。 */
247
+ setExecutionOptions(patch: Partial<ExecutionOptionsSnapshot>): void;
248
+ /** 跳转执行会话 transcript(选择会话 → current 变化 → 看板自动关闭)。 */
249
+ openSession(sessionId: string): void;
250
+ private onSessionsChanged;
251
+ /**
252
+ * 提交远程动作:按任务串行化(同任务并发动作排队,防竞态);提交期间任务
253
+ * 进入 pendingTaskIds(只做展示)。Host 拒绝 → transportError,状态不变。
254
+ * @returns Host 是否确认(快照已应用)。
255
+ */
256
+ private commitRemote;
257
+ private performRemote;
258
+ private initializeRemote;
259
+ private doInitializeRemote;
260
+ /**
261
+ * SSE 帧处理(§11.1):revision 与已应用的一致 → 就地更新 scheduler/power
262
+ * (不动任务列表,memo 边界保持);否则(帧不完整/新 revision/同步信号)重拉
263
+ * 全量 state。
264
+ */
265
+ private onRemoteEvent;
266
+ private refreshRemote;
267
+ /**
268
+ * 应用 Host 快照。同账本代数且 revision 非倒退 → 身份保持合并(memo 前提);
269
+ * 代数变化(迁移/重建)→ 整体替换。选中任务被删除/归档(非归档视图)时清除
270
+ * 选中。返回快照是否被接受。
271
+ */
272
+ private acceptRemote;
273
+ private notify;
274
+ }
@@ -0,0 +1,10 @@
1
+ import type { BoardController } from './board-controller.ts';
2
+ /** The injected board container (kept in the DOM, hidden when inactive). */
3
+ export declare const BOARD_VIEW_SELECTOR = "[data-dsh-nova-taskboard-view]";
4
+ /**
5
+ * Mount the board React tree into the center column and bind its
6
+ * visibility to the controller's boardOpen state.
7
+ * @param controller - the board controller driving the view.
8
+ * @returns disposer unmounting the tree and restoring the column.
9
+ */
10
+ export declare function mountBoard(controller: BoardController): () => void;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 对话候选任务提取集成(T011 §12.7/§14.4,B7 调研定案)。
3
+ *
4
+ * 两条提取入口:
5
+ * 1. **Agent 建议(半自动)**:订阅当前会话的对话快照(sessions.binding →
6
+ * SessionFace 的 ObservableSnapshot<ConversationSnapshot>),扫描已结算的
7
+ * assistant 节点文本块中的结构化候选标记 `⟦task-board:propose⟧ 标题 | 描述 |
8
+ * prompt`,逐条经 `propose` action 创建 proposed 候选(source=conversation
9
+ * + 来源会话/消息引用);按 (sessionId, messageId|seq, 序号) 去重,避免会话
10
+ * 快照重放/重订阅重复建候选。标记格式由 announceToAgent 系统提示播报指引
11
+ * agent 输出(§12.5/§14.4)。
12
+ * 2. **用户手动(兜底)**:向会话消息行(DOM 行 `[data-chat-anchor-key]`,键格式
13
+ * 为 DSH `conversationContextKey` 的 `${len}:${kind}${id}`)注入 hover「转为
14
+ * 任务」按钮;点击打开 ConvertTaskModal(预填消息正文,可编辑),提交为候选或
15
+ * 直接选择落列创建。注入自愈:MutationObserver 持续扫描,React 重渲染重建的
16
+ * 行会被重新注入(与 sidebar-entry 同机制,永不破坏宿主 DOM)。
17
+ *
18
+ * DSH 消息结构(B7 调研):
19
+ * - 消息节点:`ConversationNode` 判别联合(user/assistant/steering/context/…);
20
+ * user 节点 `content: ContentBlock[]`(type='text' 取文本)、assistant 节点
21
+ * `blocks: AssistantBlock[]`(kind='text' 取文本)+ `messageId?`/`seq`;
22
+ * - 节点上下文键:`conversationContextKey(kind, id)` = `${kind.length}:${kind}${id}`;
23
+ * 输入消息(user/steering)的 kind 为 `input-message`,id 为消息 id——DOM 行
24
+ * `data-chat-anchor-key` 据此识别「可转为任务」的行。
25
+ */
26
+ import type { AssistantMessageNode, ConversationSnapshot, UserMessageNode } from '@deepseek-ai/dsh-client-runtime/client';
27
+ import type { BoardController } from './board-controller.ts';
28
+ import { type ConvertTaskPrefill } from './board/ConvertTaskModal.tsx';
29
+ /** 候选标记(B7 定案:系统提示指引 agent 输出的结构化标记)。 */
30
+ export declare const PROPOSE_MARKER = "\u27E6task-board:propose\u27E7";
31
+ /** 输入消息节点定义 kind(user/steering 共用;DOM 行据此识别「转为任务」)。 */
32
+ export declare const INPUT_MESSAGE_KIND = "input-message";
33
+ /** 解析出的一条候选标记内容。 */
34
+ export interface ProposeMarkerContent {
35
+ title: string;
36
+ description: string;
37
+ prompt: string;
38
+ }
39
+ /**
40
+ * 解析文本中的候选标记(B7 定案格式 `⟦task-board:propose⟧ 标题 | 描述 | prompt`)。
41
+ * 一条文本可含多个标记(全局扫描,每个标记占据所在行);标记内容按 `|` 分段:
42
+ * 首段 = 标题(空则整条丢弃)、次段 = 描述、其余拼接为 prompt。纯函数,可脱离
43
+ * 宿主单测。
44
+ */
45
+ export declare function parseProposeMarkers(text: string): ProposeMarkerContent[];
46
+ /**
47
+ * 解析会话节点上下文键(B7:`${len}:${kind}${id}`,conversationContextKey 格式)。
48
+ * 长度前缀不匹配 / 结构非法返回 undefined。
49
+ */
50
+ export declare function parseContextKey(key: string): {
51
+ kind: string;
52
+ id: string;
53
+ } | undefined;
54
+ /** 从 user 节点内容块提取文本(ContentBlock type='text')。 */
55
+ export declare function userMessageText(node: UserMessageNode): string;
56
+ /** 从 assistant 节点块提取文本(AssistantBlock kind='text')。 */
57
+ export declare function assistantMessageText(node: AssistantMessageNode): string;
58
+ /** 会话列表 + 会话绑定的最小结构面(生产传 ctx.sessions,测试注入 fake)。 */
59
+ export interface ChatSessionsFace {
60
+ list: {
61
+ getSnapshot(): {
62
+ current?: string;
63
+ };
64
+ subscribe(listener: () => void): () => void;
65
+ };
66
+ binding(id: string): {
67
+ session: ChatSessionFace;
68
+ } | undefined;
69
+ }
70
+ /** 会话快照订阅面(生产 = SessionFace 的 ObservableSnapshot 半边)。 */
71
+ export interface ChatSessionFace {
72
+ getSnapshot(): ConversationSnapshot;
73
+ subscribe(listener: () => void): () => void;
74
+ }
75
+ /**
76
+ * 订阅当前会话,扫描 assistant 文本中的候选标记并自动 propose(§12.7 入口 1)。
77
+ * 去重键 = `${sessionId}:${messageId ?? seq}:${序号}`——会话快照重放/切换会话
78
+ * 重订阅不会重复建候选;同一 assistant 消息重复出现同一标记(如重发)视为新
79
+ * 候选(用户可 dismiss)。
80
+ * @returns disposer。
81
+ */
82
+ export declare function mountChatToTaskBoard(controller: BoardController, sessions: ChatSessionsFace): () => void;
83
+ /**
84
+ * 由消息文本派生预填:标题 = 首行(封顶 120 字)、描述 = 全文(封顶 4000),
85
+ * prompt 留空(由确认/编辑时填写)。
86
+ */
87
+ export declare function prefillFromText(text: string, messageId?: string): ConvertTaskPrefill;
88
+ /**
89
+ * 向会话消息行注入 hover「转为任务」按钮(§12.7 入口 2,B7 定案)。
90
+ *
91
+ * - 扫描 `[data-conversation-scroll]` 容器内的 `[data-chat-anchor-key]` 行;
92
+ * - 键解析出 kind = `input-message`(user/steering)的行才注入(注入按钮的
93
+ * 稳定标识 data-dsh-nova-taskboard-convert 幂等);
94
+ * - 自愈:MutationObserver 持续扫描——React 重渲染重建的行重新注入,被宿主
95
+ * 移除的按钮随行消失(不破坏宿主 DOM);
96
+ * - 点击:取当前会话快照的节点文本(回退行文本)打开 ConvertTaskModal。
97
+ * @returns disposer。
98
+ */
99
+ export declare function mountMessageConvertButton(controller: BoardController, sessions: ChatSessionsFace): () => void;