@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.
- package/README.md +86 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +5641 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +4873 -0
- package/lib/types/client/NovaTaskBoardSettingsCard.d.ts +17 -0
- package/lib/types/client/PlaceholderBoard.d.ts +9 -0
- package/lib/types/client/apply-guard.d.ts +25 -0
- package/lib/types/client/board/ConfirmDialog.d.ts +12 -0
- package/lib/types/client/board/ConfirmTaskDialog.d.ts +8 -0
- package/lib/types/client/board/ConvertTaskModal.d.ts +17 -0
- package/lib/types/client/board/NewTaskModal.d.ts +6 -0
- package/lib/types/client/board/RequirementSplitModal.d.ts +14 -0
- package/lib/types/client/board/SplitConfirmDialog.d.ts +9 -0
- package/lib/types/client/board/TagEditor.d.ts +6 -0
- package/lib/types/client/board/TaskBoard.d.ts +9 -0
- package/lib/types/client/board/TaskCard.d.ts +52 -0
- package/lib/types/client/board/TaskDetail.d.ts +7 -0
- package/lib/types/client/board/status-key.d.ts +8 -0
- package/lib/types/client/board-controller.d.ts +274 -0
- package/lib/types/client/board-mount.d.ts +10 -0
- package/lib/types/client/chat-integration.d.ts +99 -0
- package/lib/types/client/drag-utils.d.ts +78 -0
- package/lib/types/client/filter.d.ts +42 -0
- package/lib/types/client/grouping.d.ts +54 -0
- package/lib/types/client/host-api.d.ts +62 -0
- package/lib/types/client/index.d.ts +46 -0
- package/lib/types/client/legacy-store.d.ts +28 -0
- package/lib/types/client/locales.d.ts +202 -0
- package/lib/types/client/schedule-presets.d.ts +20 -0
- package/lib/types/client/sidebar-entry-core.d.ts +54 -0
- package/lib/types/client/sidebar-entry.d.ts +19 -0
- package/lib/types/core/context.d.ts +139 -0
- package/lib/types/core/cron.d.ts +54 -0
- package/lib/types/core/migrate.d.ts +39 -0
- package/lib/types/core/model.d.ts +446 -0
- package/lib/types/core/recovery.d.ts +25 -0
- package/lib/types/core/split.d.ts +81 -0
- package/lib/types/core/transitions.d.ts +436 -0
- package/lib/types/dsh-home.d.ts +21 -0
- package/lib/types/host-automation.d.ts +59 -0
- package/lib/types/host-ledger.d.ts +146 -0
- package/lib/types/host-routes.d.ts +65 -0
- package/lib/types/host-runner.d.ts +177 -0
- package/lib/types/host-service.d.ts +291 -0
- package/lib/types/index.d.ts +90 -0
- package/lib/types/loopback.d.ts +23 -0
- package/lib/types/mount-once.d.ts +9 -0
- package/lib/types/protocol.d.ts +210 -0
- package/package.json +89 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["css","useState","css","useId","useState","useRef","css","useState","useId","css","useState","useId","css","useState","useId","css","css","memo","css","useState","useId","memo","useCallback","css","useState","useRef","css","createRoot","useState","css","createRoot","css","mountSidebarEntry","mountSidebarEntryCore","useState"],"sources":["../../../packages/dsh-nova-ui-task-board/src/client/board-controller.ts","../../../packages/dsh-nova-ui-task-board/src/client/apply-guard.ts","../../../packages/dsh-nova-ui-task-board/src/client/grouping.ts","../../../packages/dsh-nova-ui-task-board/src/client/drag-utils.ts","../../../packages/dsh-nova-ui-task-board/src/client/filter.ts","../../../packages/dsh-nova-ui-task-board/src/client/locales.ts","../../../packages/dsh-nova-ui-task-board/src/core/model.ts","../../../packages/dsh-nova-ui-task-board/src/client/board/ConfirmDialog.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/ConfirmTaskDialog.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/TagEditor.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/NewTaskModal.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/RequirementSplitModal.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/status-key.ts","../../../packages/dsh-nova-ui-task-board/src/client/board/SplitConfirmDialog.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/TaskCard.tsx","../../../packages/dsh-nova-ui-task-board/src/core/cron.ts","../../../packages/dsh-nova-ui-task-board/src/client/schedule-presets.ts","../../../packages/dsh-nova-ui-task-board/src/client/board/TaskDetail.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/TaskBoard.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board-mount.tsx","../../../packages/dsh-nova-ui-task-board/src/client/board/ConvertTaskModal.tsx","../../../packages/dsh-nova-ui-task-board/src/client/chat-integration.tsx","../../../packages/dsh-nova-ui-task-board/src/protocol.ts","../../../packages/dsh-nova-ui-task-board/src/client/host-api.ts","../../../packages/dsh-nova-ui-task-board/src/client/legacy-store.ts","../../../packages/dsh-nova-ui-task-board/src/client/sidebar-entry-core.ts","../../../packages/dsh-nova-ui-task-board/src/client/sidebar-entry.ts","../../../packages/dsh-nova-ui-task-board/src/client/NovaTaskBoardSettingsCard.tsx","../../../packages/dsh-nova-ui-task-board/src/client/index.ts"],"sourcesContent":["/**\n * 看板控制器(T004):Host 账本的浏览器投影 + 视图状态 + 实时同步。\n *\n * 设计要点(DSH-REQ-001 §4-7/§8/§17):\n * - **浏览器 = 异步视图,Host = 唯一事实源**:所有变更经 `{requestId, action}`\n * 信封提交(T003 协议),UI 只认 Host 确认后的快照;`pendingTaskIds` 只做\n * 展示(spinner/禁用),不先写未确认状态(§4-7/§17 一致性);\n * - **实时同步(§11.1)**:SSE 帧 `{revision, scheduler, power}`——revision\n * 与当前一致时仅就地更新 scheduler/power(不触碰任务列表),否则重拉全量\n * state;页面重新可见/断线重连(传输层 onopen)同样触发重拉;\n * - **按任务 memo 化的前提**:`acceptRemote` 对快照做「身份保持合并」——同账本\n * 代数(ledgerId)+ 非倒退 revision 下,JSON 未变化的任务行保留对象引用,\n * 兄弟卡片状态变化只替换变化的那张,memo 边界(TaskCard)因此真正生效;\n * - **会话导航**:打开看板时基线化当前会话,会话列表 current 变化即自动关闭\n * 看板(§12.1:点击会话/工作区/搜索结果行回到会话);详情页「查看会话」经\n * sessions.open 跳转 transcript;\n * - **执行选项**:工作区列表与 agent 预设来自运行时(非账本),控制器只转发;\n * - **能力门**:`executionCapable`(run/rerun 是否已接线)。T004 基线为 false\n * (协议 run/rerun 归 T005),详情页据此禁用执行按钮并提示;T005 起恒 true\n * (协议已并入 run/rerun,Host 端 runner 恒接线)。\n * - **排序/分组视图(T009 §12.10)**:`groupMode` 视图状态(手动顺序/按项目分组/\n * 按标签分组,会话级记忆、刷新重置,不落账);`reorderTask` 提交列内排序/分组\n * 落位(status 一致性 + order 重算由 Host 守卫)、`moveTask` 扩展可选 order\n * (跨列拖拽落位,缺省追加目标列末尾)。拖拽采用「本地 pending 渲染 + Host 确认\n * 落定、失败回滚」——控制器仍不先写未确认状态,pending 位置由 UI 层以快照为基\n * 础做展示(§17 一致性:以 Host 确认的 order 为准)。\n * - **对话候选流转(T011 §11.2/§14.4,P3.1)**:`proposeTask`(对话提取/手动转\n * 任务 → proposed 候选,输入带来源会话/消息引用)、`confirmTask`(确认候选 →\n * 落 backlog/todo,confirm 是唯一 promote 路径)、`dismissTask`(拒绝/忽略删除);\n * `proposedCountOf` 提供入口未确认角标计数(§12.7)。\n * - **需求拆分(T012 §11.2/§12.9/§14.5,P3.2)**:`startRequirementSplit`(提交\n * 需求文本/文件路径 → Host 启动独立拆分会话,结算后父需求任务 + 批量候选进\n * 「待确认」列,source=requirement、children parentId 指向父任务、递归拆分挂\n * 到原任务下);批量确认/拒绝复用 `confirmTask`/`dismissTask`(T011 闸门)。\n *\n * 框架无关(结构面注入 transport/sessions/时钟/uuid),整段编排可脱离宿主单测。\n */\n\nimport type { NewTaskInput, ProposeTaskInput, SchedulerSnapshot, SplitRequestInput, TaskRecord, TaskStatus, TaskUpdatePatch } from '../core/model.ts'\nimport type { GroupMode } from './grouping.ts'\nimport type {\n TaskBoardAction,\n TaskBoardEventPayload,\n TaskBoardPowerSnapshot,\n TaskBoardSnapshot,\n} from '../protocol.ts'\n\n/** 浏览器 ↔ Host 传输面(见 host-api.ts 的 HttpTaskBoardHostTransport)。 */\nexport interface TaskBoardTransport {\n bootstrap(legacy: readonly TaskRecord[]): Promise<TaskBoardSnapshot>\n state(): Promise<TaskBoardSnapshot>\n action(action: TaskBoardAction): Promise<TaskBoardSnapshot>\n subscribe(listener: (event?: TaskBoardEventPayload) => void): () => void\n}\n\n/** 会话列表的读取面(结构面:只读 current + 订阅)。 */\nexport interface SessionsListFace {\n getSnapshot(): { current: string | undefined }\n subscribe(listener: () => void): () => void\n}\n\n/** 会话导航面:打开一个会话即切回对话视图(同时关闭看板)。 */\nexport interface SessionsFace {\n list: SessionsListFace\n open(id: string): void\n}\n\n/** 工作区下拉选项(运行时事实)。 */\nexport interface ExecutionWorkspaceOption {\n workspaceId: string\n /** 展示名(工作区标题;无标题时回退路径)。 */\n title: string\n}\n\n/** agent 预设下拉选项(运行时事实)。 */\nexport interface ExecutionPresetOption {\n id: string\n name?: string\n description?: string\n /** 该预设为何不能组装会话(下拉禁用并展示)。 */\n broken?: string\n isDefault: boolean\n}\n\n/** 执行目标三件套(工作区/模式/权限)的选项集。 */\nexport interface ExecutionOptionsSnapshot {\n workspaces: readonly ExecutionWorkspaceOption[]\n presets: readonly ExecutionPresetOption[]\n}\n\n/** 控制器对外快照(UI 只读;订阅后整体替换)。 */\nexport interface BoardSnapshot {\n tasks: readonly TaskRecord[]\n boardOpen: boolean\n /** true = 归档视图(单列),false = 五列主看板。 */\n archiveView: boolean\n /** 列内排序视图模式(T009 §12.10):手动顺序/按项目分组/按标签分组;会话级记忆、刷新重置。 */\n groupMode: GroupMode\n selectedTaskId?: string\n executionOptions: ExecutionOptionsSnapshot\n /** 已提交、等待 Host 确认的任务 id(只做展示,不先写状态)。 */\n pendingTaskIds: readonly string[]\n transportError?: string\n /** Host 元信息:revision + scheduler(时区)+ power。 */\n host?: { revision: number; scheduler: SchedulerSnapshot; power: TaskBoardPowerSnapshot }\n /** run/rerun 是否已接线(T005 前恒 false;详情页据此禁用执行按钮)。 */\n executionCapable: boolean\n}\n\n/** 当前选中的任务(按 selectedTaskId 从账本解析),无则 undefined。 */\nexport function selectedTaskOf(snapshot: BoardSnapshot): TaskRecord | undefined {\n if (snapshot.selectedTaskId === undefined) return undefined\n return snapshot.tasks.find(task => task.id === snapshot.selectedTaskId)\n}\n\n/** 未确认候选任务数(T011 §12.7 入口角标:proposed 且未归档)。 */\nexport function proposedCountOf(snapshot: BoardSnapshot): number {\n let count = 0\n for (const task of snapshot.tasks) {\n if (task.status === 'proposed' && task.archivedAt === undefined) count += 1\n }\n return count\n}\n\n/** 控制器依赖(全部结构面,测试可注入 fake)。 */\nexport interface BoardControllerDeps {\n transport: TaskBoardTransport\n sessions: SessionsFace\n /** 时钟(测试注入)。 */\n now?: () => number\n /** id 铸造(测试注入)。 */\n uuid?: () => string\n /**\n * v1 只读回退数据的惰性读取(§12 范围项 8):bootstrap 时传入 transport 做\n * 一次性迁移。缺省不读(测试/无 v1 环境)。生产接线读 localStorage。\n */\n legacy?: () => readonly TaskRecord[]\n}\n\nfunction randomUuid(): string {\n const randomUUID = globalThis.crypto?.randomUUID\n if (randomUUID !== undefined) return randomUUID.call(globalThis.crypto!)\n return `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * 身份保持合并:同一账本代数且非倒退 revision 下,JSON 未变化的任务行保留\n * 原对象引用——兄弟卡片状态变化不会全量替换任务对象,TaskCard 的 memo 边界\n * 生效(§12.2 卡片按任务粒度 memo 化)。代数变化/倒退则整体替换。\n */\nfunction mergeTasksIdentityPreserving(\n prev: readonly TaskRecord[],\n next: readonly TaskRecord[],\n preserve: boolean,\n): TaskRecord[] {\n if (!preserve) return [...next]\n const prevById = new Map<string, TaskRecord>()\n for (const task of prev) prevById.set(task.id, task)\n return next.map(task => {\n const old = prevById.get(task.id)\n return old !== undefined && JSON.stringify(old) === JSON.stringify(task) ? old : task\n })\n}\n\n/**\n * 看板控制器(见模块注释)。所有变更 bump 快照并通知订阅者;UI 与 DOM 挂载\n * 通过订阅重渲染。生产路径恒为 Host 背书(transport 必传)。\n */\nexport class BoardController {\n private tasks: TaskRecord[] = []\n private boardOpen = false\n private archiveView = false\n private groupMode: GroupMode = 'manual'\n private selectedTaskId: string | undefined\n private executionOptions: ExecutionOptionsSnapshot = { workspaces: [], presets: [] }\n private readonly listeners = new Set<() => void>()\n private readonly disposers: Array<() => void> = []\n private readonly now: () => number\n private readonly uuid: () => string\n private readonly pendingTaskIds = new Set<string>()\n private readonly taskQueues = new Map<string, Promise<void>>()\n private transportError: string | undefined\n private hostState: BoardSnapshot['host'] | undefined\n private remoteSubscribed = false\n private remoteInitialization: Promise<boolean> | undefined\n private started = false\n private lastCurrent: string | undefined\n\n /** @param deps - 传输、会话导航面、时钟与 id 铸造。 */\n constructor(private readonly deps: BoardControllerDeps) {\n this.now = deps.now ?? (() => Date.now())\n this.uuid = deps.uuid ?? randomUuid\n }\n\n // --- lifecycle -------------------------------------------------------------\n\n /** 启动:初始化 Host 同步 + 订阅 SSE + 会话导航。幂等。 */\n start(): void {\n if (this.started) return\n this.started = true\n void this.initializeRemote()\n this.disposers.push(this.deps.sessions.list.subscribe(() => { this.onSessionsChanged() }))\n this.notify()\n }\n\n /** 停止全部订阅并释放状态(幂等)。 */\n dispose(): void {\n for (const dispose of this.disposers.splice(0)) dispose()\n this.listeners.clear()\n }\n\n // --- snapshot / subscription ------------------------------------------------\n\n getSnapshot(): BoardSnapshot {\n return {\n tasks: this.tasks,\n boardOpen: this.boardOpen,\n archiveView: this.archiveView,\n groupMode: this.groupMode,\n ...(this.selectedTaskId === undefined ? {} : { selectedTaskId: this.selectedTaskId }),\n executionOptions: this.executionOptions,\n pendingTaskIds: [...this.pendingTaskIds],\n executionCapable: true, // T005 起协议已并入 run/rerun,Host 恒接线执行驱动\n ...(this.transportError === undefined ? {} : { transportError: this.transportError }),\n ...(this.hostState === undefined ? {} : { host: this.hostState }),\n }\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /** 生产变更是否由 Host 传输确认(T004 恒 true;保留语义供测试/回退判断)。 */\n isHostBacked(): boolean {\n return this.deps.transport !== undefined\n }\n\n /** 显式重试与 Host 的首次/全量同步(错误条「重试」按钮)。 */\n async retryHostSync(): Promise<boolean> {\n return await this.initializeRemote()\n }\n\n // --- view state ---------------------------------------------------------------\n\n openBoard(): void {\n if (this.boardOpen) return\n // 基线化打开时点的当前会话:之后任何会话切换(点击会话/工作区/搜索结果行)\n // 都关闭看板回到对话(§12.1);仅状态更新不改变 current,不影响看板。\n this.lastCurrent = this.deps.sessions.list.getSnapshot().current\n this.boardOpen = true\n this.notify()\n }\n\n closeBoard(): void {\n if (!this.boardOpen) return\n this.boardOpen = false\n this.notify()\n }\n\n toggleBoard(): void {\n if (this.boardOpen) this.closeBoard()\n else this.openBoard()\n }\n\n /**\n * 切换主看板/归档视图。离开归档视图时若选中任务仍是归档任务则清除选中\n * ——详情不得悬停在已离开看板的任务上。\n */\n toggleArchiveView(): void {\n this.archiveView = !this.archiveView\n if (!this.archiveView && this.selectedTaskId !== undefined) {\n const selected = this.tasks.find(task => task.id === this.selectedTaskId)\n if (selected?.archivedAt !== undefined) this.selectedTaskId = undefined\n }\n this.notify()\n }\n\n /**\n * 切换列内排序视图模式(T009 §12.10):手动顺序/按项目分组/按标签分组。\n * 分组是视图层能力,不改底层 order 与归属数据;模式仅会话级记忆(刷新重置)。\n */\n setGroupMode(mode: GroupMode): void {\n if (this.groupMode === mode) return\n this.groupMode = mode\n this.notify()\n }\n\n openTask(id: string): void {\n if (this.tasks.some(task => task.id === id)) {\n this.selectedTaskId = id\n this.notify()\n }\n }\n\n closeTask(): void {\n if (this.selectedTaskId === undefined) return\n this.selectedTaskId = undefined\n this.notify()\n }\n\n // --- task mutations(全部经 Host action 协议,UI 不先写未确认状态) ------------\n\n /**\n * 经 Host 创建任务;**只有 Host 确认后**才在账本可见并返回任务(NewTaskModal\n * 据此决定关闭/重试)。失败返回 undefined 并置 transportError。\n */\n async createTaskConfirmed(input: NewTaskInput): Promise<TaskRecord | undefined> {\n const id = this.uuid()\n const confirmed = await this.commitRemote({ kind: 'create', id, input }, id)\n return confirmed ? this.tasks.find(task => task.id === id) : undefined\n }\n\n /**\n * propose(T011 §11.2/§14.4):对话提取/手动转任务 → 创建 proposed 候选任务。\n * 输入沿用 NewTaskInput + sourceConversationId/sourceMessageId(来源引用写入\n * metadata,可跳回对话);清洗与命令字段拒绝在 Host(transitions/protocol)。\n * 只有 Host 确认后才在账本可见并返回候选;失败返回 undefined 并置 transportError。\n */\n async proposeTask(input: ProposeTaskInput): Promise<TaskRecord | undefined> {\n const id = this.uuid()\n const confirmed = await this.commitRemote({ kind: 'propose', id, input }, id)\n return confirmed ? this.tasks.find(task => task.id === id) : undefined\n }\n\n /**\n * confirm(T011 §10.2/§11.2):确认候选任务 → 落所选列(backlog/todo)。\n * confirm 是候选进入正式看板的唯一路径(防注入闸门)。返回 Host 是否确认。\n */\n confirmTask(id: string, target: 'backlog' | 'todo'): Promise<boolean> {\n return this.commitRemote({ kind: 'confirm', taskId: id, target }, id)\n }\n\n /** dismiss(T011 §10.2/§11.2):拒绝/忽略候选任务(删除)。 */\n dismissTask(id: string): void {\n void this.commitRemote({ kind: 'dismiss', taskId: id }, id)\n }\n\n /**\n * start-split(T012 §11.2/§12.9/§14.5):需求文档自动拆分。提交需求(标题 +\n * 粘贴文本或工作区文件路径)后,Host 启动独立拆分会话驱动 agent 按 §14.5\n * 方法论拆分;结算后父需求任务 + 批量候选进入「待确认」列(source=requirement,\n * 人工确认闸门复用 T011——confirm 仍是唯一 promote 路径)。返回 Host 是否确认\n * 受理(受理后拆分异步进行,SSE revision 变化时批量候选到达待确认列)。\n */\n async startRequirementSplit(input: SplitRequestInput): Promise<boolean> {\n return await this.commitRemote({ kind: 'start-split', id: this.uuid(), input })\n }\n\n updateTask(id: string, patch: TaskUpdatePatch): void {\n void this.commitRemote({ kind: 'update', taskId: id, patch }, id)\n }\n\n /**\n * 跨列移动(§10.2/§11.2 + T009 order 扩展):backlog/todo 互移、done/failed\n * 拖回 backlog/todo 为重开(仅改状态不触发执行,区别于 rerun)。`order` 可选\n * = 目标列落位下标(0-based,缺省追加目标列末尾);同列拖拽排序请用\n * `reorderTask`。返回 Host 是否确认。\n */\n moveTask(id: string, status: TaskStatus, order?: number): Promise<boolean> {\n return this.commitRemote({ kind: 'move', taskId: id, status, ...(order === undefined ? {} : { order }) }, id)\n }\n\n /**\n * 列内排序/分组落位(T009 §11.2 reorder):调整列内 order(`status` 须与任务\n * 当前状态一致,Host 防竞态校验);`order` = 0-based 落位下标(Host 重算同列\n * 唯一)。分组视图跨组落位可携带归属变更 `ownership`(`project` 空串清除 /\n * `tags` 整体替换),与 order 原子落账。返回 Host 是否确认。\n */\n reorderTask(\n id: string,\n status: TaskStatus,\n order: number,\n ownership?: { project?: string; tags?: string[] },\n ): Promise<boolean> {\n return this.commitRemote({\n kind: 'reorder',\n taskId: id,\n status,\n order,\n ...(ownership?.project !== undefined ? { project: ownership.project } : {}),\n ...(ownership?.tags !== undefined ? { tags: ownership.tags } : {}),\n }, id)\n }\n\n deleteTask(id: string): void {\n void this.commitRemote({ kind: 'delete', taskId: id }, id)\n }\n\n /** 归档已结算任务(done/failed)。Host 守卫拒绝非法归档并置错误条。 */\n archiveTask(id: string): void {\n void this.commitRemote({ kind: 'archive', taskId: id }, id)\n }\n\n /** 恢复归档任务回原列。确认后关闭详情(任务已离开归档视图)。 */\n restoreTask(id: string): void {\n void this.commitRemote({ kind: 'restore', taskId: id }, id).then(restored => {\n if (restored && this.selectedTaskId === id) this.closeTask()\n })\n }\n\n /**\n * 执行任务(T005 §13.2):协议 `run` 开执行记录,Host 端 runner 异步创建\n * 会话并发送 Prompt;本方法只提交,结算由 Host 轮询完成。\n */\n async runTask(id: string): Promise<boolean> {\n return await this.commitRemote({ kind: 'run', taskId: id }, id)\n }\n\n /** 重新执行(§13.2):协议 `rerun`,Host 侧先落回 todo 再开启执行。 */\n async rerunTask(id: string): Promise<void> {\n await this.commitRemote({ kind: 'rerun', taskId: id }, id)\n }\n\n /**\n * 设定/修改/关闭任务的 cron 周期定时(T006 §11.2 set-schedule)。patch 只含\n * enabled/cron;nextRunAt/lastTriggeredAt 由 Host 调度器维护(浏览器不可写,\n * §9.4)。Host 拒绝(非法 cron / 归档 / proposed 等)→ transportError,账本不变。\n */\n setSchedule(id: string, patch: { enabled?: boolean; cron?: string }): void {\n void this.commitRemote({ kind: 'set-schedule', taskId: id, patch }, id)\n }\n\n /**\n * 设定/修改/清除任务的一次性计划(T007 §11.2 set-one-shot)。patch 只含\n * runAt(缺省或 0 = 取消);firedAt 由 Host 调度器维护(浏览器不可写,\n * §9.4)。设定自动清除 cron 分支、反之亦然(互斥 D6)。Host 拒绝(非有限\n * runAt / 归档 / proposed 等)→ transportError,账本不变。\n */\n setOneShot(id: string, patch: { runAt?: number }): void {\n void this.commitRemote({ kind: 'set-one-shot', taskId: id, patch }, id)\n }\n\n /** 替换执行选项集的一部分(工作区列表/预设名册来自运行时,非账本)。 */\n setExecutionOptions(patch: Partial<ExecutionOptionsSnapshot>): void {\n this.executionOptions = { ...this.executionOptions, ...patch }\n this.notify()\n }\n\n /** 跳转执行会话 transcript(选择会话 → current 变化 → 看板自动关闭)。 */\n openSession(sessionId: string): void {\n this.deps.sessions.open(sessionId)\n }\n\n // --- internals ---------------------------------------------------------------\n\n private onSessionsChanged(): void {\n if (!this.boardOpen) return\n const current = this.deps.sessions.list.getSnapshot().current\n if (current !== this.lastCurrent) this.closeBoard()\n this.lastCurrent = current\n }\n\n /**\n * 提交远程动作:按任务串行化(同任务并发动作排队,防竞态);提交期间任务\n * 进入 pendingTaskIds(只做展示)。Host 拒绝 → transportError,状态不变。\n * @returns Host 是否确认(快照已应用)。\n */\n private async commitRemote(action: TaskBoardAction, taskId?: string): Promise<boolean> {\n const transport = this.deps.transport\n if (transport === undefined) return true\n if (taskId === undefined) return await this.performRemote(action)\n const previous = this.taskQueues.get(taskId) ?? Promise.resolve()\n const operation = previous.catch(() => {}).then(async () => await this.performRemote(action))\n const tail = operation.then(() => {}, () => {})\n this.taskQueues.set(taskId, tail)\n this.pendingTaskIds.add(taskId)\n this.notify()\n try {\n return await operation\n } finally {\n if (this.taskQueues.get(taskId) === tail) {\n this.taskQueues.delete(taskId)\n this.pendingTaskIds.delete(taskId)\n this.notify()\n }\n }\n }\n\n private async performRemote(action: TaskBoardAction): Promise<boolean> {\n const transport = this.deps.transport\n if (transport === undefined) return true\n this.transportError = undefined\n this.notify()\n try {\n return this.acceptRemote(await transport.action(action))\n || await this.refreshRemote()\n } catch (error) {\n await this.refreshRemote(messageOf(error))\n return false\n }\n }\n\n private async initializeRemote(): Promise<boolean> {\n if (this.remoteInitialization !== undefined) return await this.remoteInitialization\n const initialization = this.doInitializeRemote()\n this.remoteInitialization = initialization\n try {\n return await initialization\n } finally {\n if (this.remoteInitialization === initialization) this.remoteInitialization = undefined\n }\n }\n\n private async doInitializeRemote(): Promise<boolean> {\n const transport = this.deps.transport\n if (transport === undefined) return true\n try {\n this.acceptRemote(await transport.bootstrap(this.deps.legacy?.() ?? []))\n if (!this.remoteSubscribed) {\n this.remoteSubscribed = true\n this.disposers.push(transport.subscribe(event => { this.onRemoteEvent(event) }))\n }\n return true\n } catch (error) {\n this.transportError = messageOf(error)\n this.notify()\n return false\n }\n }\n\n /**\n * SSE 帧处理(§11.1):revision 与已应用的一致 → 就地更新 scheduler/power\n * (不动任务列表,memo 边界保持);否则(帧不完整/新 revision/同步信号)重拉\n * 全量 state。\n */\n private onRemoteEvent(event: TaskBoardEventPayload | undefined): void {\n if (event !== undefined && this.hostState !== undefined && event.revision === this.hostState.revision\n && typeof event.scheduler === 'object' && event.scheduler !== null\n && typeof event.power === 'object' && event.power !== null) {\n this.hostState = { revision: event.revision, scheduler: event.scheduler, power: event.power }\n this.notify()\n return\n }\n void this.refreshRemote()\n }\n\n private async refreshRemote(preserveError?: string): Promise<boolean> {\n const transport = this.deps.transport\n if (transport === undefined) return true\n try {\n this.acceptRemote(await transport.state())\n if (preserveError !== undefined) {\n this.transportError = preserveError\n this.notify()\n }\n return true\n } catch (error) {\n this.transportError = preserveError ?? messageOf(error)\n this.notify()\n return false\n }\n }\n\n /**\n * 应用 Host 快照。同账本代数且 revision 非倒退 → 身份保持合并(memo 前提);\n * 代数变化(迁移/重建)→ 整体替换。选中任务被删除/归档(非归档视图)时清除\n * 选中。返回快照是否被接受。\n */\n private acceptRemote(snapshot: TaskBoardSnapshot): boolean {\n const current = this.hostState\n const nextLedgerId = snapshot.scheduler.ledgerId\n const sameGeneration = current === undefined || current.scheduler.ledgerId === nextLedgerId\n if (sameGeneration && current !== undefined && snapshot.revision < current.revision) return false\n const preserve = sameGeneration && current !== undefined && snapshot.revision >= current.revision\n this.tasks = mergeTasksIdentityPreserving(this.tasks, snapshot.tasks, preserve)\n this.hostState = { revision: snapshot.revision, scheduler: snapshot.scheduler, power: snapshot.power }\n this.transportError = undefined\n if (this.selectedTaskId !== undefined && !this.tasks.some(task => task.id === this.selectedTaskId)) {\n this.selectedTaskId = undefined\n }\n if (!this.archiveView && this.selectedTaskId !== undefined\n && this.tasks.find(task => task.id === this.selectedTaskId)?.archivedAt !== undefined) {\n this.selectedTaskId = undefined\n }\n this.notify()\n return true\n }\n\n private notify(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n","/**\n * Cross-module-instance apply guard for the nova task-board client bundle.\n *\n * The client factory can run more than once in a single page lifetime (for\n * example when a stale bundle is mixed with a rebuilt one while `dsh web` is\n * restarted). Without a guard, every factory run mounts its own sidebar entry\n * and board view, so the shell ends up showing two \"task board\" rows.\n *\n * The flag lives on globalThis so separate module instances (independent\n * factory runs) still share one guard. First claim wins; later claims become\n * no-ops until the claim is released (fiber unload / hot-reload) or the page\n * reloads.\n */\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __dshNovaTaskBoardApplied: boolean | undefined\n}\n\n/** Claims the plugin apply slot. Returns true when this call won the slot. */\nexport function claimNovaTaskBoardApply(): boolean {\n if (globalThis.__dshNovaTaskBoardApplied === true) return false\n globalThis.__dshNovaTaskBoardApplied = true\n return true\n}\n\n/**\n * Releases the claim. Called from the client fiber cleanup so that a\n * hot-reloaded bundle (the loader unloads the old plugin fiber and invokes\n * the rebuilt one in the same page) can claim again instead of being\n * silently dropped.\n */\nexport function releaseNovaTaskBoardApply(): void {\n globalThis.__dshNovaTaskBoardApplied = undefined\n}\n","/**\n * 看板列内分组/排序纯函数(T009 §12.10,D10)。\n *\n * - 排序视图模式(看板顶部切换,会话级记忆、刷新重置):手动顺序(默认,完全按\n * order)/ 按项目分组 / 按标签分组;\n * - 分组是视图层能力,不改底层 order 与归属数据(归属变更经 reorder/update 落账);\n * - 按项目分组:组头 = 项目名,无 `project` 的任务归「未分类」组,组内按 order\n * 升序;按标签分组:组头 = 标签,任务按全部 `tags` 多归组展示(一个任务可出现\n * 在多个标签组),无标签任务归「未分类」组,组内按 order 升序;\n * - 排序必须与 Host 端 transitions.byOrderStable 完全一致(order 升序,\n * createdAt/id 决胜),否则浏览器计算的落位下标与 Host 重算不一致;\n * - 全部为纯函数(无 fs、无 DOM、无运行时依赖),可脱离宿主单测\n * (验收 3:分组视图过滤/归组逻辑)。\n */\n\nimport type { TaskRecord } from '../core/model.ts'\n\n/** 排序视图模式(§12.10:看板顶部切换,会话级记忆、刷新重置)。 */\nexport type GroupMode = 'manual' | 'project' | 'tag'\n\n/** 全部模式(顶部切换按钮按此顺序渲染)。 */\nexport const GROUP_MODES: readonly GroupMode[] = ['manual', 'project', 'tag']\n\n/**\n * 「未分类」组的哨兵 key。项目名/标签经 trim 后非空(normalizeOptionalString /\n * normalizeTags 丢弃空白项),空串不可能与真实归属冲突,安全用作哨兵。\n */\nexport const UNGROUPED = ''\n\n/** 一个分组(组头 = label;组内任务按 order 升序)。 */\nexport interface TaskGroup {\n /** 稳定 key:项目名 / 标签名 / UNGROUPED(未分类)。 */\n key: string\n /** 展示名(未分类由调用方渲染为「未分类」文案)。 */\n label: string\n tasks: TaskRecord[]\n}\n\n/** 与 Host transitions.byOrderStable 一致的列内排序(order 升序,createdAt/id 决胜)。 */\nexport function byOrderStable(a: TaskRecord, b: TaskRecord): number {\n return a.order - b.order || a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n}\n\n/** 列内任务按 order 升序排序(原数组不改动)。 */\nexport function sortByOrder(tasks: readonly TaskRecord[]): TaskRecord[] {\n return [...tasks].sort(byOrderStable)\n}\n\n/** 任务在给定视图模式下的组 key(手动模式无组概念,返回 undefined)。 */\nexport function groupKeyOf(task: TaskRecord, mode: GroupMode): string | undefined {\n if (mode === 'project') return task.project ?? UNGROUPED\n if (mode === 'tag') return task.tags.length > 0 ? task.tags[0] : UNGROUPED\n return undefined\n}\n\n/** 任务是否属于某个组(按模式解释 key:项目精确匹配 / 标签命中 / 未分类无归属)。 */\nexport function groupContains(task: TaskRecord, groupKey: string, mode: GroupMode): boolean {\n if (mode === 'project') return groupKey === UNGROUPED ? task.project === undefined : task.project === groupKey\n if (mode === 'tag') {\n return groupKey === UNGROUPED ? task.tags.length === 0 : task.tags.includes(groupKey)\n }\n return false\n}\n\n/**\n * 拖拽目标组的归属变换(视图层预览用,与提交的 reorder 归属补丁一致,§12.10):\n * - 按项目分组:拖到组 G → project = G;拖到「未分类」→ 清除 project;\n * - 按标签分组:拖到组 G → G 成为首选标签(tags[0],G 已含于 tags 时仅调整顺序);\n * 拖到「未分类」→ 移除首选标签(tags.shift(),若 tags 为空则归「未分类」);\n * - 手动模式或非组落位 → 原样返回。\n */\nexport function applyOwnershipTransform(task: TaskRecord, groupKey: string | undefined, mode: GroupMode): TaskRecord {\n if (mode === 'manual' || groupKey === undefined) return task\n if (mode === 'project') {\n return groupKey === UNGROUPED ? { ...task, project: undefined } : { ...task, project: groupKey }\n }\n if (groupKey === UNGROUPED) {\n return { ...task, tags: task.tags.slice(1) }\n }\n return { ...task, tags: [groupKey, ...task.tags.filter(tag => tag !== groupKey)] }\n}\n\n/**\n * 分组(§12.10):项目/标签模式下的归组展示,组内按 order 升序;标签模式多归组\n * (一个任务可出现在其全部标签组),无标签归「未分类」;未分类组恒在最后。\n * 手动模式不分组(调用方直接 sortByOrder)。\n */\nexport function groupTasks(tasks: readonly TaskRecord[], mode: Exclude<GroupMode, 'manual'>): TaskGroup[] {\n if (mode === 'project') {\n const byProject = new Map<string, TaskRecord[]>()\n for (const task of tasks) {\n const key = task.project ?? UNGROUPED\n const list = byProject.get(key)\n if (list === undefined) byProject.set(key, [task])\n else list.push(task)\n }\n return groupsFromMap(byProject)\n }\n // tag 模式:任务按其全部 tags 多归组;无标签归「未分类」。\n const byTag = new Map<string, TaskRecord[]>()\n const ungrouped: TaskRecord[] = []\n for (const task of tasks) {\n if (task.tags.length === 0) {\n ungrouped.push(task)\n continue\n }\n for (const tag of task.tags) {\n const list = byTag.get(tag)\n if (list === undefined) byTag.set(tag, [task])\n else list.push(task)\n }\n }\n return [\n ...groupsFromMap(byTag),\n ...(ungrouped.length > 0 ? [{ key: UNGROUPED, label: '', tasks: sortByOrder(ungrouped) }] : []),\n ]\n}\n\n/** 按 label(zh locale)排序的组列表;「未分类」组(key 空串)恒在最后。 */\nfunction groupsFromMap(map: Map<string, TaskRecord[]>): TaskGroup[] {\n const groups: TaskGroup[] = []\n for (const [key, list] of map) {\n if (key === UNGROUPED) continue\n groups.push({ key, label: key, tasks: sortByOrder(list) })\n }\n groups.sort((a, b) => a.label.localeCompare(b.label, 'zh'))\n const ungrouped = map.get(UNGROUPED)\n if (ungrouped !== undefined) groups.push({ key: UNGROUPED, label: '', tasks: sortByOrder(ungrouped) })\n return groups\n}\n","/**\n * 看板拖拽纯函数(T009 §12.10,D10)。\n *\n * - 合法性(§10.2/§11.2):running 不可作为拖拽来源/目标;跨列拖拽 = move,\n * 目标仅限 backlog/todo(backlog↔todo 互移、done/failed 拖回 backlog/todo 为\n * 重开);同列拖拽 = reorder(各列均可列内排序);拖到 running 等非法目标被\n * 拒绝并提示;\n * - 落位下标语义(与 Host transitions.applyReposition 一致):浏览器提交的\n * `order` = 0-based 插入下标,基准是**剔除拖拽卡片后的目标列**(按 order 稳定\n * 升序)——锚定卡片(before/after)或组尾/列尾(end)换算成该列表的下标;\n * 过滤状态下「列尾」按可见列表最后一张卡在基准列表中的位置定位(先过滤后排序\n * /分组展示,§12.10);\n * - action 构建:同列 → `reorder`(分组视图跨组落位携带归属变更:项目模式\n * project=组名/''(清除);标签模式 tags 使目标标签成为首选或移除首选);\n * 跨列 → `move`(可选 order 落位,done/failed 重开由状态机守卫);\n * - 全部为纯函数,可脱离宿主单测(验收 3)。\n */\n\nimport type { TaskRecord, TaskStatus } from '../core/model.ts'\nimport type { TaskBoardAction } from '../protocol.ts'\nimport { applyOwnershipTransform, groupContains, sortByOrder, UNGROUPED, type GroupMode } from './grouping.ts'\n\n/** 拖拽提交的 action 判别联合(buildDropAction 只产出 reorder/move 两种)。 */\nexport type DropAction =\n | Extract<TaskBoardAction, { kind: 'reorder' }>\n | Extract<TaskBoardAction, { kind: 'move' }>\n\n/** 拖拽来源(dragstart 时记录;分组视图记录起始组 key 以判定跨组)。 */\nexport interface DragOrigin {\n taskId: string\n fromStatus: TaskStatus\n fromTags: readonly string[]\n /** 分组视图下起始组的 key('' = 未分类);手动模式 undefined。 */\n fromGroupKey?: string\n}\n\n/** 落位方式:锚定卡片之前/之后,或组尾/列尾。 */\nexport type DropPosition = 'before' | 'after' | 'end'\n\n/** 拖拽目标(dragover/drop 时计算)。 */\nexport interface DropTarget {\n /** 目标列。 */\n status: TaskStatus\n /** 分组视图目标组的 key('' = 未分类);手动模式 undefined。 */\n groupKey?: string\n /** 锚定卡片 id(落在卡片上时);否则为组尾/列尾。 */\n anchorId?: string\n /** 落位方式:锚定卡片存在时 before/after;否则 end。 */\n position: DropPosition\n}\n\n/**\n * 拖拽合法性(§10.2/§11.2):running 不可作目标;同列 = 列内排序;跨列目标仅限\n * backlog/todo(done/failed 拖回为重开)。proposed/归档不可作为来源由调用方\n * (卡片 draggable=false)保证。\n */\nexport function isLegalDrop(origin: DragOrigin, targetStatus: TaskStatus): boolean {\n if (targetStatus === 'running') return false\n if (targetStatus === origin.fromStatus) return true\n return targetStatus === 'backlog' || targetStatus === 'todo'\n}\n\n/** 目标组尾在基准列(按 order 升序)中的最后一张卡的下标;组空返回 -1。 */\nfunction lastGroupIndex(column: readonly TaskRecord[], groupKey: string, mode: GroupMode): number {\n let last = -1\n for (let index = 0; index < column.length; index += 1) {\n if (groupContains(column[index], groupKey, mode)) last = index\n }\n return last\n}\n\n/**\n * 计算落位下标(0-based,基准 = 剔除拖拽卡片后的目标列,按 order 稳定升序;\n * 与 Host applyReposition 的 insert 基准一致):\n * - 锚定卡片:卡片下标(after +1);\n * - 分组视图组尾:目标组最后一张卡下标 +1(组空回退列尾);\n * - 列尾:可见列表最后一张卡下标 +1(过滤定位;无可见卡则列尾)。\n */\nexport function computeDropIndex(\n restColumn: readonly TaskRecord[],\n target: DropTarget,\n mode: GroupMode,\n lastVisibleId?: string,\n): number {\n if (target.anchorId !== undefined) {\n const anchor = restColumn.findIndex(task => task.id === target.anchorId)\n if (anchor !== -1) return anchor + (target.position === 'after' ? 1 : 0)\n }\n if (target.groupKey !== undefined && mode !== 'manual') {\n const last = lastGroupIndex(restColumn, target.groupKey, mode)\n if (last !== -1) return last + 1\n }\n if (lastVisibleId !== undefined) {\n const index = restColumn.findIndex(task => task.id === lastVisibleId)\n if (index !== -1) return index + 1\n }\n return restColumn.length\n}\n\n/**\n * 可见列表内的落位下标(拖拽 pending 渲染用;list = 目标列/目标组的可见列表,\n * **已剔除拖拽卡片**):锚定卡片 before/after,否则列表尾。\n */\nexport function visibleDropIndex(list: readonly TaskRecord[], target: DropTarget): number {\n if (target.anchorId !== undefined) {\n const index = list.findIndex(task => task.id === target.anchorId)\n if (index !== -1) return Math.min(index + (target.position === 'after' ? 1 : 0), list.length)\n }\n return list.length\n}\n\n/**\n * 构建拖拽提交的 action(§11.2/D10):\n * - 同列 → `reorder`(status 与来源一致;分组视图跨组落位携带归属变更——\n * 项目模式 project = 组名 / ''(清除);标签模式 tags 使目标标签成为首选或\n * 移除首选(未分类));\n * - 跨列 → `move`(status 为目标列 + 落位下标,缺省语义由 Host 追加末尾;\n * done/failed 重开不触发执行)。\n * 非法目标/拖到自己返回 undefined(调用方提示拒绝)。\n */\nexport function buildDropAction(\n origin: DragOrigin,\n target: DropTarget,\n mode: GroupMode,\n tasks: readonly TaskRecord[],\n lastVisibleId?: string,\n): DropAction | undefined {\n if (!isLegalDrop(origin, target.status)) return undefined\n if (target.anchorId === origin.taskId) return undefined\n const task = tasks.find(candidate => candidate.id === origin.taskId)\n if (task === undefined) return undefined\n const restColumn = sortByOrder(tasks.filter(candidate => candidate.status === target.status))\n .filter(candidate => candidate.id !== origin.taskId)\n const index = computeDropIndex(restColumn, target, mode, lastVisibleId)\n if (target.status === origin.fromStatus) {\n // 同列:reorder(分组视图跨组落位携带归属变更,与 order 原子落账)。\n let project: string | undefined\n let tags: string[] | undefined\n if (mode === 'project' && target.groupKey !== undefined && target.groupKey !== origin.fromGroupKey) {\n project = target.groupKey === UNGROUPED ? '' : target.groupKey\n } else if (mode === 'tag' && target.groupKey !== undefined && target.groupKey !== origin.fromGroupKey) {\n tags = target.groupKey === UNGROUPED\n ? task.tags.slice(1)\n : [target.groupKey, ...task.tags.filter(tag => tag !== target.groupKey)]\n }\n return {\n kind: 'reorder',\n taskId: origin.taskId,\n status: target.status,\n order: index,\n ...(project !== undefined ? { project } : {}),\n ...(tags !== undefined ? { tags } : {}),\n }\n }\n // 跨列:move(落位目标列指定位置;done/failed 拖回 backlog/todo 为重开)。\n return { kind: 'move', taskId: origin.taskId, status: target.status, order: index }\n}\n\n/** 拖拽目标组的归属变换后的行(pending 渲染预览,与提交的 reorder 补丁一致)。 */\nexport function pendingTaskView(task: TaskRecord, target: DropTarget, mode: GroupMode): TaskRecord {\n return applyOwnershipTransform(task, target.groupKey, mode)\n}\n","/**\n * 看板过滤纯函数(T008 §12.8,D9)。\n *\n * - 过滤维度:文本搜索(标题/描述,大小写不敏感)+ 项目(单选)+ 标签(多选);\n * - 语义:多条件(文本/项目/标签)之间 AND;同一条件内标签为 OR(命中任一\n * 标签即通过);项目为单选精确匹配(`none` = 仅无项目任务);\n * - 过滤跨列生效(主看板/归档视图由调用方先筛视图再调本函数);过滤状态仅\n * 会话级记忆(刷新重置,不做持久化,P4 再考虑);\n * - 全部为纯函数(无 fs、无 DOM、无运行时依赖),可脱离宿主单测\n * (验收 3:过滤逻辑纯函数)。\n */\n\nimport type { TaskRecord } from '../core/model.ts'\n\n/** 项目过滤值:`all` = 不限;`none` = 仅无项目任务;`name` = 精确项目名。 */\nexport type ProjectFilterValue =\n | { kind: 'all' }\n | { kind: 'none' }\n | { kind: 'name'; name: string }\n\n/** 过滤状态(会话级记忆;刷新重置,不做持久化)。 */\nexport interface TaskFilter {\n /** 文本搜索(标题/描述,大小写不敏感;空 = 不限制)。 */\n query: string\n /** 项目过滤(缺省/`all` = 不限)。 */\n project: ProjectFilterValue\n /** 标签过滤:空数组 = 不限;非空 = 命中任一标签(同一条件内 OR)。 */\n tags: readonly string[]\n}\n\n/** 空过滤(看板初始状态)。 */\nexport const EMPTY_FILTER: TaskFilter = { query: '', project: { kind: 'all' }, tags: [] }\n\n/** 单条任务是否命中过滤(多条件 AND、标签内 OR)。 */\nexport function matchesFilter(task: TaskRecord, filter: TaskFilter): boolean {\n const query = filter.query.trim().toLowerCase()\n if (query !== ''\n && !task.title.toLowerCase().includes(query)\n && !task.description.toLowerCase().includes(query)) {\n return false\n }\n if (filter.project.kind === 'none') {\n if (task.project !== undefined) return false\n } else if (filter.project.kind === 'name') {\n if (task.project !== filter.project.name) return false\n }\n if (filter.tags.length > 0 && !filter.tags.some(tag => task.tags.includes(tag))) {\n return false\n }\n return true\n}\n\n/** 对任务集执行过滤(保持入参顺序与对象引用,memo 边界不受影响)。 */\nexport function filterTasks(tasks: readonly TaskRecord[], filter: TaskFilter): TaskRecord[] {\n return tasks.filter(task => matchesFilter(task, filter))\n}\n\n/** 过滤是否处于激活状态(任一维度有约束;用于「清除全部」显隐与命中计数)。 */\nexport function isFilterActive(filter: TaskFilter): boolean {\n return filter.query.trim() !== '' || filter.project.kind !== 'all' || filter.tags.length > 0\n}\n\n/** 从任务集推导去重后的项目名列表(过滤下拉选项),按 zh locale 排序。 */\nexport function distinctProjects(tasks: readonly TaskRecord[]): string[] {\n const set = new Set<string>()\n for (const task of tasks) {\n if (task.project !== undefined) set.add(task.project)\n }\n return [...set].sort((a, b) => a.localeCompare(b, 'zh'))\n}\n\n/** 从任务集推导去重后的标签列表(多选 chips 选项),按 zh locale 排序。 */\nexport function distinctTags(tasks: readonly TaskRecord[]): string[] {\n const set = new Set<string>()\n for (const task of tasks) {\n for (const tag of task.tags) set.add(tag)\n }\n return [...set].sort((a, b) => a.localeCompare(b, 'zh'))\n}\n","/**\n * Nova task-board copy: zh-first dictionaries with an English fallback,\n * selected by the document language. Kept dependency-free (no dsh locale\n * service) so the DOM-injected entry row and the standalone board tree share\n * one tiny lookup. Key set 以 zh 为准,en 对齐(对齐参考实现的组织方式)。\n *\n * T004 补全看板/详情/新建/状态/执行设置文案;定时编辑器(T006/T007)与\n * 待确认列(T011)的编辑/确认文案随对应任务补充。\n */\n\n/** zh dictionary (key-set source of truth). */\nexport const zh = {\n 'entry.label': '任务看板',\n 'board.title': '任务看板',\n 'board.close': '返回会话',\n 'board.new': '新建任务',\n 'board.search': '筛选任务…',\n 'board.empty': '这个状态还没有任务',\n 'board.archive': '归档',\n 'board.archiveView': '归档 ({count})',\n 'board.backToBoard': '返回看板',\n 'archive.empty': '没有已归档的任务',\n 'board.status': '状态',\n 'board.status.proposed': '待确认',\n 'board.status.backlog': '待规划',\n 'board.status.todo': '待办',\n 'board.status.running': '进行中',\n 'board.status.done': '已完成',\n 'board.status.failed': '已失败',\n 'board.runs': '次执行',\n 'board.pending': '正在提交',\n 'board.updated': '更新于',\n 'board.created': '创建于',\n 'board.hostError': 'Host 操作失败:{error}',\n 'board.retryHost': '重试连接 Host',\n 'board.hostMeta': 'Host 时区 {timeZone} · revision {revision}',\n 'new.title': '标题',\n 'new.titlePlaceholder': '一句话描述要做什么',\n 'new.description': '描述',\n 'new.descriptionPlaceholder': '补充背景、范围与验收(可选)',\n 'new.prompt': '执行 Prompt',\n 'new.promptPlaceholder': '发给 agent 的完整指令(留空则使用标题)',\n 'new.submit': '创建',\n 'new.cancel': '取消',\n 'new.required': '标题不能为空',\n 'detail.title': '任务详情',\n 'detail.close': '关闭',\n 'detail.prompt': '执行 Prompt',\n 'detail.description': '描述',\n 'detail.execution': '执行记录',\n 'detail.noExecution': '尚未执行',\n 'detail.run': '执行',\n 'detail.rerun': '重新执行',\n 'detail.delete': '删除',\n 'detail.archive': '归档',\n 'detail.restore': '恢复',\n 'detail.archivedAt': '已归档 · {time}',\n 'detail.viewSession': '查看会话',\n 'detail.executionStarted': '已启动',\n 'detail.executionEnded': '已结束',\n 'detail.result.succeeded': '成功',\n 'detail.result.failed': '失败',\n 'detail.result.cancelled': '已取消',\n 'detail.result.running': '进行中',\n 'delete.title': '删除任务',\n 'delete.confirm': '确定删除「{name}」吗?删除后不可恢复。',\n 'delete.ok': '删除',\n 'delete.cancel': '取消',\n 'status.move.backlog': '移到待规划',\n 'status.move.todo': '移到待办',\n 'time.justNow': '刚刚',\n 'card.scheduled': '定时',\n 'card.oneShot': '计划',\n 'detail.schedule': '执行安排',\n 'detail.schedule.nextRun': '下次运行',\n 'detail.schedule.lastTriggered': '上次运行',\n 'detail.schedule.notScheduled': '未安排',\n 'detail.schedule.dueSoon': '即将运行',\n 'detail.schedule.enable': '启用定时执行',\n 'detail.schedule.cron': 'cron 表达式',\n 'detail.schedule.invalid': 'cron 表达式不合法',\n 'detail.schedule.presets': '预设',\n 'detail.schedule.preset.daily9': '每天 09:00',\n 'detail.schedule.preset.hourly': '每小时整点',\n 'detail.schedule.preset.tenMin': '每 10 分钟',\n 'detail.schedule.preset.weeklyMon9': '每周一 09:00',\n 'detail.schedule.modeCron': '周期定时',\n 'detail.schedule.modeOneShot': '一次性计划',\n 'detail.oneShot.runAt': '计划时刻',\n 'detail.oneShot.remaining': '剩余',\n 'detail.oneShot.cancel': '取消计划',\n 'detail.oneShot.executed': '已执行 · {time}',\n 'detail.oneShot.skipped': '已过期/已跳过',\n 'detail.oneShot.reschedule': '可重新设定',\n 'detail.oneShot.invalid': '计划时刻不合法',\n 'detail.oneShot.dueSoon': '即将触发',\n 'detail.oneShot.preset.tenMin': '10 分钟后',\n 'detail.oneShot.preset.tonight21': '今晚 21:00',\n 'detail.oneShot.preset.tomorrow9': '明天 09:00',\n 'detail.executionSettings': '执行设置',\n 'exec.hint': '执行时生效:工作区决定执行会话落在哪个工作区;模式决定会话的 agent 预设;权限经 /permission 命令应用到会话。留空则使用运行时默认。',\n 'new.workspace': '工作区',\n 'new.mode': '模式',\n 'new.permission': '权限',\n 'exec.workspace.recent': '最近使用(默认)',\n 'exec.mode.default': '部署默认',\n 'exec.mode.defaultSuffix': '(默认)',\n 'exec.mode.brokenSuffix': '(不可用)',\n 'exec.mode.removed': '(已移除)',\n 'exec.permission.default': '会话默认',\n 'exec.permission.read-only': '只读',\n 'exec.permission.workspace-write': '工作区可写',\n 'exec.permission.danger-full-access': '完全访问',\n 'filter.project': '项目',\n 'filter.projectAll': '全部项目',\n 'filter.projectNone': '未分类(无项目)',\n 'filter.tags': '标签',\n 'filter.clearAll': '清除全部',\n 'filter.hits': '命中 {count}',\n 'filter.removeTag': '删除标签 {tag}',\n 'board.group.label': '排序/分组',\n 'board.group.manual': '手动顺序',\n 'board.group.project': '按项目分组',\n 'board.group.tag': '按标签分组',\n 'board.group.ungrouped': '未分类',\n 'board.dragRejected': '不能拖拽到该列(running 等非法目标)',\n 'detail.projectTags': '项目与标签',\n 'detail.projectPlaceholder': '所属项目(可留空)',\n 'new.tagsPlaceholder': '输入标签后回车',\n 'confirm.title': '确认候选任务',\n 'confirm.subtitle': '候选任务未确认前不可执行、不可定时、不可归档。可编辑后选择落列确认,或拒绝删除。',\n 'confirm.target': '确认后落列',\n 'confirm.toBacklog': '待规划(backlog)',\n 'confirm.toTodo': '待办(todo)',\n 'confirm.submit': '确认',\n 'confirm.dismiss': '拒绝',\n 'confirm.dismissTitle': '拒绝候选任务',\n 'confirm.dismissMessage': '确定拒绝「{name}」吗?该候选任务将被删除。',\n 'card.proposedBadge': '待确认',\n 'card.source': '来源',\n 'source.conversation': '对话',\n 'source.requirement': '需求拆分',\n 'entry.proposedCount': '{count} 个候选待确认',\n 'convert.title': '转为任务',\n 'convert.fromConversation': '从对话提取',\n 'convert.submitProposed': '创建为候选',\n 'convert.submitDirect': '直接创建',\n 'convert.directTarget': '落列',\n 'convert.directBacklog': '待规划',\n 'convert.directTodo': '待办',\n 'convert.proposedHint': '创建为候选:进入看板「待确认」列,确认后方可执行。',\n 'chat.extractedHint': '已从对话提取 {count} 个候选任务(看板「待确认」列),可随时确认或拒绝。',\n 'detail.jumpToConversation': '跳回对话',\n 'detail.sourceConversation': '来源会话',\n 'split.entry': '从需求拆分',\n 'split.subtitle': '提交一份需求文档(粘贴文本或工作区文件路径),系统将主动拆分为一组可独立执行/验收的子任务候选,进入「待确认」列;确认后子任务可独立执行并保留父需求引用。',\n 'split.recursiveTitle': '递归拆分',\n 'split.recursiveHint': '将针对该任务继续拆分:新建父需求任务「需求:<标题>」挂到其下,子任务再指向新父任务。',\n 'split.requirementTitle': '需求标题',\n 'split.requirementTitlePlaceholder': '如「XX 系统登录模块」',\n 'split.source': '需求来源',\n 'split.sourceText': '粘贴文本',\n 'split.sourceFile': '工作区文件路径',\n 'split.text': '需求文本',\n 'split.textPlaceholder': '粘贴需求文档内容…',\n 'split.file': '文件路径',\n 'split.filePlaceholder': '相对工作区的文件路径,如 docs/requirement.md',\n 'split.required': '请填写需求标题与需求内容(文本或文件路径)',\n 'split.submit': '发起拆分',\n 'split.asyncHint': '提交后在独立会话中拆分,完成后进入「待确认」列(未确认前不可执行)。',\n 'split.batchButton': '批量确认',\n 'split.batchTitle': '批量确认候选任务',\n 'split.batchSubtitle': '逐条编辑(标题/描述/Prompt/项目/标签)、增删条目;或批量确认到所选列、整体拒绝。',\n 'split.addItem': '添加条目',\n 'split.added': '新增',\n 'split.removeItem': '移除',\n 'split.confirmAll': '确认全部到{target}',\n 'split.dismissAll': '全部拒绝',\n 'split.dismissAllTitle': '拒绝全部候选',\n 'split.dismissAllMessage': '确定拒绝全部 {count} 个候选任务吗?将被删除。',\n 'split.dismissItemTitle': '移除候选任务',\n 'split.dismissItemMessage': '确定移除「{name}」吗?该候选任务将被删除。',\n 'split.empty': '没有可批量确认的候选任务',\n 'split.parentOf': '所属需求',\n 'split.parentMissing': '(父任务已删除)',\n 'split.children': '子任务',\n 'settings.title': '任务看板',\n 'settings.description': '控制 Host 任务看板与 agent 播报。',\n 'settings.enabled': '启用任务看板',\n 'settings.enabledHint': '关闭后隐藏侧边栏入口与看板视图。',\n 'settings.announceToAgent': '向 agent 播报任务看板',\n 'settings.announceToAgentHint': '开启:每条 agent 系统提示都会包含本看板的说明;关闭:不播报。',\n 'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置。',\n 'settings.readOnly': '当前部署的设置只读。',\n} satisfies Record<string, string>\n\n/** en dictionary, complete against the zh key set. */\nexport const en: Record<keyof typeof zh, string> = {\n 'entry.label': 'Task Board',\n 'board.title': 'Task Board',\n 'board.close': 'Back to chat',\n 'board.new': 'New Task',\n 'board.search': 'Filter tasks…',\n 'board.empty': 'No tasks in this column',\n 'board.archive': 'Archive',\n 'board.archiveView': 'Archived ({count})',\n 'board.backToBoard': 'Back to board',\n 'archive.empty': 'No archived tasks',\n 'board.status': 'Status',\n 'board.status.proposed': 'Proposed',\n 'board.status.backlog': 'Backlog',\n 'board.status.todo': 'To Do',\n 'board.status.running': 'In Progress',\n 'board.status.done': 'Done',\n 'board.status.failed': 'Failed',\n 'board.runs': 'runs',\n 'board.pending': 'Submitting',\n 'board.updated': 'Updated',\n 'board.created': 'Created',\n 'board.hostError': 'Host action failed: {error}',\n 'board.retryHost': 'Retry Host connection',\n 'board.hostMeta': 'Host time zone {timeZone} · revision {revision}',\n 'new.title': 'Title',\n 'new.titlePlaceholder': 'What should be done, in one line',\n 'new.description': 'Description',\n 'new.descriptionPlaceholder': 'Background, scope, acceptance criteria (optional)',\n 'new.prompt': 'Run Prompt',\n 'new.promptPlaceholder': 'The full instruction sent to the agent (title is used when blank)',\n 'new.submit': 'Create',\n 'new.cancel': 'Cancel',\n 'new.required': 'Title is required',\n 'detail.title': 'Task Detail',\n 'detail.close': 'Close',\n 'detail.prompt': 'Run Prompt',\n 'detail.description': 'Description',\n 'detail.execution': 'Execution History',\n 'detail.noExecution': 'Not executed yet',\n 'detail.run': 'Run',\n 'detail.rerun': 'Run Again',\n 'detail.delete': 'Delete',\n 'detail.archive': 'Archive',\n 'detail.restore': 'Restore',\n 'detail.archivedAt': 'Archived · {time}',\n 'detail.viewSession': 'View Session',\n 'detail.executionStarted': 'Started',\n 'detail.executionEnded': 'Ended',\n 'detail.result.succeeded': 'Succeeded',\n 'detail.result.failed': 'Failed',\n 'detail.result.cancelled': 'Cancelled',\n 'detail.result.running': 'Running',\n 'delete.title': 'Delete Task',\n 'delete.confirm': 'Delete \"{name}\"? This cannot be undone.',\n 'delete.ok': 'Delete',\n 'delete.cancel': 'Cancel',\n 'status.move.backlog': 'Move to Backlog',\n 'status.move.todo': 'Move to To Do',\n 'time.justNow': 'just now',\n 'card.scheduled': 'scheduled',\n 'card.oneShot': 'planned',\n 'detail.schedule': 'Schedule',\n 'detail.schedule.nextRun': 'Next run',\n 'detail.schedule.lastTriggered': 'Last run',\n 'detail.schedule.notScheduled': 'Not scheduled',\n 'detail.schedule.dueSoon': 'Due soon',\n 'detail.schedule.enable': 'Enable scheduled runs',\n 'detail.schedule.cron': 'cron expression',\n 'detail.schedule.invalid': 'Invalid cron expression',\n 'detail.schedule.presets': 'Presets',\n 'detail.schedule.preset.daily9': 'Daily 09:00',\n 'detail.schedule.preset.hourly': 'Every hour',\n 'detail.schedule.preset.tenMin': 'Every 10 min',\n 'detail.schedule.preset.weeklyMon9': 'Mon 09:00',\n 'detail.schedule.modeCron': 'Recurring',\n 'detail.schedule.modeOneShot': 'One-shot',\n 'detail.oneShot.runAt': 'Run at',\n 'detail.oneShot.remaining': 'in',\n 'detail.oneShot.cancel': 'Cancel plan',\n 'detail.oneShot.executed': 'Executed · {time}',\n 'detail.oneShot.skipped': 'Expired / skipped',\n 'detail.oneShot.reschedule': 'reschedule',\n 'detail.oneShot.invalid': 'Invalid plan time',\n 'detail.oneShot.dueSoon': 'Due soon',\n 'detail.oneShot.preset.tenMin': 'In 10 minutes',\n 'detail.oneShot.preset.tonight21': 'Tonight 21:00',\n 'detail.oneShot.preset.tomorrow9': 'Tomorrow 09:00',\n 'detail.executionSettings': 'Execution Settings',\n 'exec.hint': 'Applied when the task runs: the workspace decides where the execution session lands; the mode composes the session\\'s agent preset; the permission is applied through the /permission command. Blank = runtime default.',\n 'new.workspace': 'Workspace',\n 'new.mode': 'Mode',\n 'new.permission': 'Permission',\n 'exec.workspace.recent': 'Most recent (default)',\n 'exec.mode.default': 'Deployment default',\n 'exec.mode.defaultSuffix': ' (default)',\n 'exec.mode.brokenSuffix': ' (unavailable)',\n 'exec.mode.removed': ' (removed)',\n 'exec.permission.default': 'Session default',\n 'exec.permission.read-only': 'Read-only',\n 'exec.permission.workspace-write': 'Workspace Write',\n 'exec.permission.danger-full-access': 'Full Access',\n 'filter.project': 'Project',\n 'filter.projectAll': 'All projects',\n 'filter.projectNone': 'Unassigned',\n 'filter.tags': 'Tags',\n 'filter.clearAll': 'Clear all',\n 'filter.hits': '{count} hits',\n 'filter.removeTag': 'Remove tag {tag}',\n 'board.group.label': 'Sort & group',\n 'board.group.manual': 'Manual',\n 'board.group.project': 'By project',\n 'board.group.tag': 'By tag',\n 'board.group.ungrouped': 'Unassigned',\n 'board.dragRejected': 'Cannot drop here (illegal target)',\n 'detail.projectTags': 'Project & Tags',\n 'detail.projectPlaceholder': 'Project (optional)',\n 'new.tagsPlaceholder': 'Type a tag and press Enter',\n 'confirm.title': 'Confirm candidate task',\n 'confirm.subtitle': 'Candidates cannot run, be scheduled, or be archived until confirmed. Edit and pick a target column, or dismiss.',\n 'confirm.target': 'Confirm into',\n 'confirm.toBacklog': 'Backlog',\n 'confirm.toTodo': 'To Do',\n 'confirm.submit': 'Confirm',\n 'confirm.dismiss': 'Dismiss',\n 'confirm.dismissTitle': 'Dismiss candidate task',\n 'confirm.dismissMessage': 'Dismiss \"{name}\"? The candidate task will be deleted.',\n 'card.proposedBadge': 'Pending',\n 'card.source': 'Source',\n 'source.conversation': 'Chat',\n 'source.requirement': 'Requirement',\n 'entry.proposedCount': '{count} candidate(s) pending',\n 'convert.title': 'Turn into task',\n 'convert.fromConversation': 'From conversation',\n 'convert.submitProposed': 'Propose',\n 'convert.submitDirect': 'Create directly',\n 'convert.directTarget': 'Into',\n 'convert.directBacklog': 'Backlog',\n 'convert.directTodo': 'To Do',\n 'convert.proposedHint': 'Proposing adds the task to the \"Pending\" column; it runs only after confirmation.',\n 'chat.extractedHint': 'Extracted {count} candidate task(s) from the conversation (see the board \"Pending\" column).',\n 'detail.jumpToConversation': 'Back to chat',\n 'detail.sourceConversation': 'Source session',\n 'split.entry': 'Split from requirement',\n 'split.subtitle': 'Submit a requirement document (pasted text or a workspace file path); the system proactively splits it into independently executable/verifiable subtask candidates in the \"Pending\" column. Confirmed subtasks run independently and keep a reference to the parent requirement.',\n 'split.recursiveTitle': 'Recursive split',\n 'split.recursiveHint': 'Continue splitting this task: a new parent requirement task \"Requirement: <title>\" is created under it, and the subtasks point to the new parent.',\n 'split.requirementTitle': 'Requirement title',\n 'split.requirementTitlePlaceholder': 'e.g. \"Login module\"',\n 'split.source': 'Requirement source',\n 'split.sourceText': 'Pasted text',\n 'split.sourceFile': 'Workspace file path',\n 'split.text': 'Requirement text',\n 'split.textPlaceholder': 'Paste the requirement document…',\n 'split.file': 'File path',\n 'split.filePlaceholder': 'Path relative to the workspace, e.g. docs/requirement.md',\n 'split.required': 'Requirement title and content (text or file path) are required',\n 'split.submit': 'Start split',\n 'split.asyncHint': 'Splitting runs in a dedicated session; results land in the \"Pending\" column (not executable until confirmed).',\n 'split.batchButton': 'Batch confirm',\n 'split.batchTitle': 'Batch confirm candidates',\n 'split.batchSubtitle': 'Edit items (title/description/prompt/project/tags), add or remove rows; confirm all into the chosen column, or dismiss all.',\n 'split.addItem': 'Add item',\n 'split.added': 'New',\n 'split.removeItem': 'Remove',\n 'split.confirmAll': 'Confirm all into {target}',\n 'split.dismissAll': 'Dismiss all',\n 'split.dismissAllTitle': 'Dismiss all candidates',\n 'split.dismissAllMessage': 'Dismiss all {count} candidate(s)? They will be deleted.',\n 'split.dismissItemTitle': 'Remove candidate',\n 'split.dismissItemMessage': 'Remove \"{name}\"? The candidate task will be deleted.',\n 'split.empty': 'No candidates to batch-confirm',\n 'split.parentOf': 'Parent requirement',\n 'split.parentMissing': '(parent task deleted)',\n 'split.children': 'Subtasks',\n 'settings.title': 'Task Board',\n 'settings.description': 'Configure the Host task board and agent announcement.',\n 'settings.enabled': 'Enable the task board',\n 'settings.enabledHint': 'When off, the sidebar entry and board view are hidden.',\n 'settings.announceToAgent': 'Announce the task board to agents',\n 'settings.announceToAgentHint': 'On: every agent system prompt includes a note about this board. Off: no announcement.',\n 'settings.notExposed': 'This DSH version does not expose this plugin\\'s settings namespace to the configuration page, so the form is unavailable. Edit ~/.dsh/settings.yaml directly.',\n 'settings.readOnly': 'This deployment stores settings read-only.',\n}\n\n/** The dictionary key union. */\nexport type NovaTaskBoardKey = keyof typeof zh\n\n/** Active dictionary, picked by the document language at call time. */\nexport function dictionary(): Record<NovaTaskBoardKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/** Translate a key with optional {name} template params. */\nexport function t(key: NovaTaskBoardKey, params?: Record<string, string>): string {\n let text: string = dictionary()[key]\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, value)\n }\n }\n return text\n}\n","/**\n * Task board authoritative data model(DSH-REQ-001 §9)。\n *\n * T002 落地完整的 Host 侧数据模型:\n * - 账本文档(§9.1):`LedgerDocument`,`schemaVersion: 2`、单调递增 `revision`、\n * `tasks`、`scheduler`(timeZone/ledgerId/lastTickAt/error/importedSources)、\n * `recentRequests`(最近 256 条幂等缓存);\n * - 任务(§9.2):`TaskRecord` 全字段。`tags/project/order/parentId` 本期落位\n * (默认值/归一化),对应能力分别由 T008/T009/T012 启用;`source` 字段随\n * 对话流转(P3)提前启用;\n * - 执行记录(§9.3):`ExecutionRecord`;\n * - 执行安排(§9.4):`ScheduleRule` 判别联合(cron | one-shot);cron 的到期\n * 计算与 one-shot 的触发消费属 T006/T007,本层只做结构解析与归一化。\n *\n * T010 补齐「自动化收集 + 上下文闭环」(P3,§9.5/§14):\n * - 评论(comments):`TaskComment`(author/body/type/createdAt)挂在任务行;\n * - 上下文快照(context_snapshot):`ContextSnapshot`\n * (goal/keyDecisions/filePaths/lastAiSummary/latestUserFeedback/relatedLinks/\n * updatedAt),每次执行后由 Host 更新,后续调用只注入摘要 + 必要片段(B5);\n * - 产物(artifacts):`Artifact`(type/title/url/contentRef),会话 transcript\n * 即默认产物(执行结算成功时自动记录);\n * - 自动化规则(automation_rules):`AutomationRule` 集合挂在账本文档\n * (trigger=5 段 cron / source=github_issue|bookmark_collector / config /\n * filter.labels / enabled),GitHub Webhook 与收藏收集 cron 共用;\n * - 来源元数据:`TaskRecord.metadata`(Record<string,string>),GitHub Issue\n * 的来源链接 / 书签 URL 等写入此处(§14.1/14.2,供去重与追溯)。\n *\n * 解析/归一化是纯函数(无 fs、无运行时依赖),保证模型可脱离宿主单测;\n * 行级损坏「修复或丢弃 schedule、绝不丢整行」,账本级损坏抛\n * `LedgerSchemaError` 由 HostLedger 走隔离路径(见 host-ledger.ts)。\n */\n\n/** 账本 schema 版本(不兼容时走损坏隔离路径,见 §9.1)。 */\nexport const LEDGER_SCHEMA_VERSION = 2 as const\n\n/** 幂等缓存保留条数(§11.3:最近 256 条)。 */\nexport const MAX_REQUEST_CACHE = 256\n\n/** 任务生命周期状态,与看板列一一对应(§10.1);`proposed` 为对话流转候选(D7/P3.1)。 */\nexport const TASK_STATUSES = ['proposed', 'backlog', 'todo', 'running', 'done', 'failed'] as const\n\n/** 任务生命周期状态。 */\nexport type TaskStatus = typeof TASK_STATUSES[number]\n\n/** 任务来源(§9.2,v1.0 字段,P3 随对话流转启用)。 */\nexport const TASK_SOURCES = [\n 'manual',\n 'conversation',\n 'github_issue',\n 'bookmark_collector',\n 'feishu',\n 'requirement',\n 'other',\n] as const\n\n/** 任务来源。 */\nexport type TaskSource = typeof TASK_SOURCES[number]\n\n/** 执行会话钉住的权限预设 id(`/permission <id>`,§9.2)。 */\nexport const TASK_PERMISSIONS = ['read-only', 'workspace-write', 'danger-full-access'] as const\n\n/** 执行会话钉住的权限预设 id。 */\nexport type TaskPermission = typeof TASK_PERMISSIONS[number]\n\n/** 执行结果(§9.3)。 */\nexport const EXECUTION_RESULTS = ['succeeded', 'failed', 'cancelled'] as const\n\n/** 执行结果。 */\nexport type ExecutionResult = typeof EXECUTION_RESULTS[number]\n\n/** 评论类型(§9.5 comments:user_feedback/ai_log/system_event)。 */\nexport const COMMENT_TYPES = ['user_feedback', 'ai_log', 'system_event'] as const\n\n/** 评论类型。 */\nexport type CommentType = typeof COMMENT_TYPES[number]\n\n/** 产物类型(§9.5 artifacts;会话 transcript 即默认产物 `session`)。 */\nexport const ARTIFACT_TYPES = ['session', 'link', 'file', 'other'] as const\n\n/** 产物类型。 */\nexport type ArtifactType = typeof ARTIFACT_TYPES[number]\n\n/** 自动化收集来源(§14.1/14.2 automation_rules.source)。 */\nexport const AUTOMATION_SOURCES = ['github_issue', 'bookmark_collector'] as const\n\n/** 自动化收集来源。 */\nexport type AutomationSource = typeof AUTOMATION_SOURCES[number]\n\n/** 单次执行尝试(§9.3):自己的 id、承载会话(创建后回填)、起止与结算结果。 */\nexport interface ExecutionRecord {\n /** 执行尝试 id(uuid)。 */\n id: string\n /** 承载本次执行的 DSH 会话 id;会话创建后回填,运行中/启动前缺省。 */\n sessionId?: string\n /** 开始时间(ms epoch)。 */\n startedAt: number\n /** 结算时间(ms epoch);运行中缺省。 */\n endedAt?: number\n /** 结算结果;运行中缺省。 */\n result?: ExecutionResult\n /** 失败/取消原因。 */\n error?: string\n}\n\n/** 任务评论(§9.5 comments:author/body/type/createdAt,P3/T010 落地)。 */\nexport interface TaskComment {\n /** 评论 id(uuid)。 */\n id: string\n /** 作者(缺省 'user';'system' 为 Host 侧系统事件)。 */\n author: string\n /** 正文(已清洗:控制字符剥离、长度封顶)。 */\n body: string\n /** 评论类型(§9.5)。 */\n type: CommentType\n /** 创建时间(ms epoch)。 */\n createdAt: number\n}\n\n/** 产物记录(§9.5 artifacts:type/title/url/content_ref;会话 transcript 即默认产物)。 */\nexport interface Artifact {\n /** 产物 id(uuid)。 */\n id: string\n /** 产物类型(session/link/file/other)。 */\n type: ArtifactType\n /** 展示标题。 */\n title: string\n /** 外链(link 类)。 */\n url?: string\n /** 内容引用(如会话 id / 文件相对路径)。 */\n contentRef?: string\n /** 记录时间(ms epoch)。 */\n createdAt: number\n}\n\n/**\n * 上下文快照(§9.5 context_snapshot,P3/T010 落地)。\n * 每次执行后由 Host 更新(lastAiSummary 来自会话最近一条 assistant 消息的\n * 文本摘取、latestUserFeedback 来自最近一条 user_feedback 评论、relatedLinks\n * 来自来源 metadata.url);goal/keyDecisions/filePaths 由用户/后续任务维护。\n * 后续调用只注入摘要 + 必要片段(见 core/context.ts 的 composeInjectionContext,\n * B5 调研决定 Host 侧摘取 + 固定 token 预算)。\n */\nexport interface ContextSnapshot {\n /** 任务目标(可空;初始可由任务标题/描述派生)。 */\n goal?: string\n /** 关键决策记录(P3 起,供跨会话续接)。 */\n keyDecisions: string[]\n /** 涉及文件路径(供续接定位)。 */\n filePaths: string[]\n /** 最近一次 AI 执行摘要(Host 执行结算时摘取,长度受 B5 预算约束)。 */\n lastAiSummary?: string\n /** 最近一条用户反馈(评论 type=user_feedback 的最新正文)。 */\n latestUserFeedback?: string\n /** 相关链接(来源 metadata.url 等)。 */\n relatedLinks: string[]\n /** 快照更新时间(ms epoch)。 */\n updatedAt: number\n}\n\n/**\n * 自动化收集规则(§9.5 automation_rules / §14.1-14.3,P3/T010 落地)。\n * 挂在账本文档级(非任务行):一条规则定义一个外部来源的定时收集行为。\n * - trigger:5 段 cron(收藏收集默认每周日 00:00;GitHub 也可配置轮询兜底);\n * `nextRunAt`/`lastTriggeredAt` 为 Host 独占字段(tick 扫描与收集后维护,\n * 浏览器不可写),与任务 schedule 的 cron 分支同语义(错过跳过不补跑);\n * - source:`github_issue`(GitHub Webhook 推送 + 可选 API 轮询)或\n * `bookmark_collector`(本地收藏收件箱文件,见 host-automation.ts);\n * - config:来源级配置(github_issue:`repo`;bookmark_collector:`filePath`);\n * - filter.labels:GitHub 标签过滤(非空时 Issue 必须命中至少一个标签才收集,\n * §14.1「如仅 bug/enhancement 标签」)。\n */\nexport interface AutomationRule {\n /** 规则 id(uuid,浏览器创建时生成)。 */\n id: string\n /** 是否武装(false = 不参与 tick 扫描与 Webhook 过滤)。 */\n enabled: boolean\n /** 收集来源。 */\n source: AutomationSource\n /** 触发:5 段 cron + Host 独占的到期/最近触发时间戳。 */\n trigger: { kind: 'cron'; cron: string; nextRunAt?: number; lastTriggeredAt?: number }\n /** 来源级配置(均为字符串值)。 */\n config: Record<string, string>\n /** 过滤器(GitHub 标签;空数组 = 全部接受)。 */\n filter: { labels: string[] }\n /** 创建时间(ms epoch)。 */\n createdAt: number\n /** 最后变更时间(ms epoch)。 */\n updatedAt: number\n}\n\n/**\n * 执行安排(§9.4 判别联合):一个任务同时只持有一个执行安排,cron 周期定时\n * 或一次性计划(one-shot)二选一。判别联合延续协议层的严格校验风格。\n */\nexport type ScheduleRule =\n | {\n kind: 'cron'\n /** 是否武装。 */\n enabled: boolean\n /** 5 段 cron(分 时 日 月 周),Host 本地时区。 */\n cron: string\n /** 下次到期点(ms epoch,Host 调度器维护,浏览器不可写)。 */\n nextRunAt?: number\n /** 最近触发点(ms epoch)。 */\n lastTriggeredAt?: number\n }\n | {\n kind: 'one-shot'\n /** 计划执行时刻(ms epoch,Host 本地时区)。 */\n runAt: number\n /** 触发/消费标记(ms epoch):到点触发执行时写入,防止重启后重复触发。 */\n firedAt?: number\n }\n\n/** 看板上的一个任务(§9.2)。 */\nexport interface TaskRecord {\n /** 稳定任务 id(uuid),全局唯一。 */\n id: string\n /** 显示标题(非空,创建时校验)。 */\n title: string\n /** 长描述,展示于详情页(可空)。 */\n description: string\n /** 执行时发送给 agent 的 Prompt;空则回退用标题。 */\n prompt: string\n /** 当前状态(列)。 */\n status: TaskStatus\n /** 任务来源(P3 随对话流转启用;`requirement` 为需求拆分产物)。 */\n source?: TaskSource\n /** 创建时间(ms epoch)。 */\n createdAt: number\n /** 最后变更时间(ms epoch)。 */\n updatedAt: number\n /** 全部执行记录,最近的在最后。 */\n executions: ExecutionRecord[]\n /** 可选执行安排(cron 或 one-shot,二选一,见 §9.4)。 */\n schedule?: ScheduleRule\n /** 钉住的工作区(空 = 执行时用最近工作区)。 */\n workspaceId?: string\n /** 钉住的 agent 预设 id(空 = 部署默认)。 */\n mode?: string\n /** 钉住的权限预设(空 = 会话默认)。 */\n permission?: TaskPermission\n /** 归档标记(ms epoch);归档任务离开主看板、只读,定时被解除。 */\n archivedAt?: number\n /** 标签数组(模块/优先级/技术栈等),默认 [](D9,P2.6 起启用)。 */\n tags: string[]\n /** 所属项目名(D9,P2.6 起启用)。 */\n project?: string\n /** 父任务 id(需求拆分子任务指向父需求任务,D8,P3.2 起启用)。 */\n parentId?: string\n /**\n * 列内排序键:同列内唯一(不要求连续,Host 在 reorder/move 时重算,D10,\n * P2.7 起启用);新任务追加到所在列末尾。\n */\n order: number\n /** 来源元数据(T010,§14.1/14.2):GitHub Issue 链接 / 书签 URL 等;去重与追溯用。 */\n metadata?: Record<string, string>\n /** 评论(T010,§9.5 comments;默认 [])。 */\n comments: TaskComment[]\n /** 产物记录(T010,§9.5 artifacts;默认 [])。 */\n artifacts: Artifact[]\n /** 上下文快照(T010,§9.5 context_snapshot;每次执行后更新)。 */\n contextSnapshot?: ContextSnapshot\n}\n\n/**\n * 新建任务的输入(§11.2 `create` / §12.4 新建表单)。\n * T003 基线不含执行安排(cron/one-shot 属 T006/T007);T008 扩展 project/tags\n * (§9.2,新建表单可选填项目与标签,缺省不落字段/[])。\n */\nexport interface NewTaskInput {\n /** 显示标题(非空,trim 后校验)。 */\n title: string\n /** 长描述(可空)。 */\n description: string\n /** 执行时发送给 agent 的 Prompt;空则回退用标题。 */\n prompt: string\n /** 钉住的工作区(空/缺省 = 执行时用最近工作区)。 */\n workspaceId?: string\n /** 钉住的 agent 预设 id(空/缺省 = 部署默认)。 */\n mode?: string\n /** 钉住的权限预设(缺省 = 会话默认)。 */\n permission?: TaskPermission\n /** 所属项目名(T008,可空;trim 后落字段)。 */\n project?: string\n /** 任意标签(T008,缺省 [];按 normalizeTags 规范化落账)。 */\n tags?: string[]\n}\n\n/**\n * 任务可编辑字段补丁(§11.2 `update`)。浏览器不可写 Host/runner 独占字段:\n * `schedule.*`、`executions[].result/endedAt`、`archivedAt`、`status` 等一律\n * 不在补丁面内(协议层 exact keys 拒绝)。T008 扩展 project/tags(§9.2:\n * 项目单选可清除、标签数组整体替换,空数组 = 清空)。\n */\nexport type TaskUpdatePatch = Partial<Pick<\n TaskRecord,\n 'title' | 'description' | 'prompt' | 'workspaceId' | 'mode' | 'permission' | 'project' | 'tags'\n>>\n\n/**\n * 对话流转候选的输入(§11.2 `propose`,T011/P3.1):沿用 NewTaskInput 全部\n * 字段,另加来源会话/消息引用(写入 metadata,供跳回对话追溯,§9.2/§14.4)。\n * 标题/描述/prompt 的清洗(长度/字符/控制字符)由状态机落账层完成\n * (transitions.applyProposeTask,§16.7)。\n */\nexport interface ProposeTaskInput extends NewTaskInput {\n /** 来源会话 id(Agent 标记提取 / 手动「转为任务」时写入)。 */\n sourceConversationId?: string\n /** 来源消息 id(可跳回对话中具体消息;缺省时以 seq 字符串代替)。 */\n sourceMessageId?: string\n}\n\n/**\n * 批量候选条目(§11.2 `propose-batch` / §14.5,T012/P3.2):ProposeTaskInput\n * 扩展 `parentId`(父需求任务 id)。校验与落账见 transitions.applyProposeBatch:\n * parentId 必须指向账本内已存在且非自身的任务(禁止环引用,§16.8),创建后\n * 不可改(update 补丁面不含 parentId)。\n */\nexport interface BatchProposeItem extends ProposeTaskInput {\n /** 父需求任务 id(需求拆分子任务指向父任务,D8)。 */\n parentId?: string\n}\n\n/**\n * 需求拆分请求(§11.2 `start-split` / §12.9 / §14.5,T012/P3.2):用户提交一份\n * 需求文档(粘贴文本或工作区文件路径),Host 启动独立拆分会话(复用执行通道,\n * B8)驱动 agent 按 §14.5 方法论拆分,结算后经父需求任务 propose +\n * propose-batch 落账(全部 proposed,人工确认闸门复用 T011)。\n *\n * - `text` 与 `filePath` 至少提供其一(需求来源);两者都提供时 text 优先内联、\n * filePath 作为来源说明写入父任务描述;\n * - `parentTaskId`:递归拆分入口(§12.9)——对任一任务(含子任务)再发起拆分\n * 时,新建的父需求任务「需求:<标题>」以该任务为父(parentId),子任务再指向\n * 新父任务,形成树状层级。\n */\nexport interface SplitRequestInput {\n /** 需求标题(父任务「需求:<标题>」与拆分会话标题)。 */\n title: string\n /** 粘贴的需求原文(与 filePath 至少其一)。 */\n text?: string\n /** 工作区内文件相对路径(与 text 至少其一;agent 自行读取文件)。 */\n filePath?: string\n /** 需求所属项目(父任务与子任务自动带,§14.5 子任务产出结构)。 */\n project?: string\n /** 默认标签(父任务与子任务自动带;agent 可再细化,见 core/split.ts)。 */\n tags?: string[]\n /** 递归拆分:新父需求任务挂到该任务下(§12.9/§14.5)。 */\n parentTaskId?: string\n}\n\n/** 来源会话引用写入 metadata 的键(§9.2/§14.4:可跳回对话)。 */\nexport const SOURCE_CONVERSATION_META_KEY = 'sourceConversationId'\n\n/** 来源消息引用写入 metadata 的键(§9.2/§14.4:可跳回消息)。 */\nexport const SOURCE_MESSAGE_META_KEY = 'sourceMessageId'\n\n/** 账本 scheduler 对外快照(不含内部字段 importedSources,见 §11.1 events)。 */\nexport interface SchedulerSnapshot {\n /** Host 本地时区。 */\n timeZone: string\n /** 账本代数(迁移/导入判定)。 */\n ledgerId?: string\n /** 最近 cron tick(ms epoch)。 */\n lastTickAt?: number\n /** 可见的调度/恢复错误。 */\n error?: string\n}\n\n/** 账本内持久化的 scheduler(含内部字段,仅在文档层可见)。 */\nexport interface LedgerScheduler extends SchedulerSnapshot {\n /** 已导入的 v1 来源(§9.1);导入 marker 只在 Host 确认(原子落账)后写入。 */\n importedSources?: string[]\n}\n\n/** 幂等缓存条目(§11.3)。 */\nexport interface RecentRequest {\n requestId: string\n fingerprint: string\n}\n\n/** 账本文档(§9.1)。 */\nexport interface LedgerDocument {\n schemaVersion: typeof LEDGER_SCHEMA_VERSION\n /** 单调递增的版本号;每次原子落账 +1。 */\n revision: number\n /** 全部任务(含归档)。 */\n tasks: TaskRecord[]\n scheduler: LedgerScheduler\n /** 最近 256 条幂等缓存,随账本原子持久化。 */\n recentRequests: RecentRequest[]\n /** 自动化收集规则(T010,§9.5 automation_rules)。 */\n automationRules: AutomationRule[]\n}\n\n/** Host 内部状态快照(revision + tasks 深拷贝 + scheduler + automationRules,不含 power——power 属协议层)。 */\nexport interface LedgerState {\n revision: number\n tasks: TaskRecord[]\n scheduler: SchedulerSnapshot\n automationRules: AutomationRule[]\n}\n\n/** 账本级损坏(schema 不兼容/不可解析),由 HostLedger 走隔离路径。 */\nexport class LedgerSchemaError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'LedgerSchemaError'\n }\n}\n\n/** 有限数字守卫。 */\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\n/** 非空字符串(trim 后)守卫,空串/空白清除钉住字段(对齐参考实现 normalizeTargetId)。 */\nexport function normalizeOptionalString(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed === '' ? undefined : trimmed\n}\n\n/** 是否为已知任务状态。 */\nexport function isTaskStatus(value: unknown): value is TaskStatus {\n return typeof value === 'string' && (TASK_STATUSES as readonly string[]).includes(value)\n}\n\n/** 是否为已知任务来源。 */\nexport function isTaskSource(value: unknown): value is TaskSource {\n return typeof value === 'string' && (TASK_SOURCES as readonly string[]).includes(value)\n}\n\n/** 是否为已知权限预设。 */\nexport function isTaskPermission(value: unknown): value is TaskPermission {\n return typeof value === 'string' && (TASK_PERMISSIONS as readonly string[]).includes(value)\n}\n\n/** 是否为已知执行结果。 */\nexport function isExecutionResult(value: unknown): value is ExecutionResult {\n return typeof value === 'string' && (EXECUTION_RESULTS as readonly string[]).includes(value)\n}\n\n/** 是否为已知评论类型。 */\nexport function isCommentType(value: unknown): value is CommentType {\n return typeof value === 'string' && (COMMENT_TYPES as readonly string[]).includes(value)\n}\n\n/** 是否为已知产物类型。 */\nexport function isArtifactType(value: unknown): value is ArtifactType {\n return typeof value === 'string' && (ARTIFACT_TYPES as readonly string[]).includes(value)\n}\n\n/** 是否为已知自动化收集来源。 */\nexport function isAutomationSource(value: unknown): value is AutomationSource {\n return typeof value === 'string' && (AUTOMATION_SOURCES as readonly string[]).includes(value)\n}\n\n/**\n * cron 表达式的基础形状校验(§9.4:5 段 cron「分 时 日 月 周」)。\n * 这是结构级校验:段数、字符集(数字、`* , - / ?` 及名字字母)。cron 的到期\n * 计算与完整合法性由 T006 的调度器实现;本层只保证持久化的规则形状可解析。\n */\nconst CRON_FIELD_RE = /^[0-9A-Za-z*,\\-\\/?]+$/\n\nexport function isPlausibleCron(cron: unknown): cron is string {\n if (typeof cron !== 'string' || cron.trim() === '') return false\n const fields = cron.trim().split(/\\s+/)\n if (fields.length !== 5) return false\n return fields.every(field => CRON_FIELD_RE.test(field))\n}\n\n/**\n * 归一化一条持久化的执行安排(§9.4 判别联合):\n * - `kind: 'one-shot'` → one-shot 分支;`runAt` 非有限数则整条丢弃;\n * - `kind: 'cron'` 或缺省 kind(v1/v2 旧形状)→ cron 分支;cron 形状非法则整条\n * 丢弃(「修复或丢弃 schedule、绝不丢整行」——坏 schedule 不拖垮任务行);\n * - 其他 kind → 丢弃。\n */\nexport function normalizeSchedule(value: unknown): ScheduleRule | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const rule = value as Record<string, unknown>\n if (rule.kind === 'one-shot') {\n if (!isFiniteNumber(rule.runAt)) return undefined\n const schedule: ScheduleRule = { kind: 'one-shot', runAt: rule.runAt }\n if (isFiniteNumber(rule.firedAt)) schedule.firedAt = rule.firedAt\n return schedule\n }\n if (rule.kind !== undefined && rule.kind !== 'cron') return undefined\n if (!isPlausibleCron(rule.cron)) return undefined\n const schedule: ScheduleRule = { kind: 'cron', enabled: rule.enabled === true, cron: rule.cron }\n if (isFiniteNumber(rule.nextRunAt)) schedule.nextRunAt = rule.nextRunAt\n if (isFiniteNumber(rule.lastTriggeredAt)) schedule.lastTriggeredAt = rule.lastTriggeredAt\n return schedule\n}\n\n/** 归一化一条执行记录(§9.3);结构非法返回 undefined。 */\nexport function normalizeExecution(value: unknown): ExecutionRecord | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as Record<string, unknown>\n if (typeof entry.id !== 'string' || entry.id === '') return undefined\n if (!isFiniteNumber(entry.startedAt)) return undefined\n if (entry.sessionId !== undefined && typeof entry.sessionId !== 'string') return undefined\n if (entry.endedAt !== undefined && !isFiniteNumber(entry.endedAt)) return undefined\n if (entry.result !== undefined && !isExecutionResult(entry.result)) return undefined\n if (entry.error !== undefined && typeof entry.error !== 'string') return undefined\n const execution: ExecutionRecord = { id: entry.id, startedAt: entry.startedAt }\n if (typeof entry.sessionId === 'string') execution.sessionId = entry.sessionId\n if (isFiniteNumber(entry.endedAt)) execution.endedAt = entry.endedAt\n if (isExecutionResult(entry.result)) execution.result = entry.result\n if (typeof entry.error === 'string') execution.error = entry.error\n return execution\n}\n\n/**\n * 归一化标签数组(§9.2 默认 []):仅保留字符串元素,逐项 trim、丢弃空白项、\n * 按首次出现顺序去重;缺省/非法 → []。标签是展示/过滤维度的原始字符串,\n * 大小写敏感(`API` 与 `api` 是两个标签),去重不折叠大小写。\n */\nexport function normalizeTags(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n const seen = new Set<string>()\n const tags: string[] = []\n for (const tag of value) {\n if (typeof tag !== 'string') continue\n const trimmed = tag.trim()\n if (trimmed === '' || seen.has(trimmed)) continue\n seen.add(trimmed)\n tags.push(trimmed)\n }\n return tags\n}\n\n/** 通用字符串数组归一化(trim、丢空白、首现去重、条目长度封顶、数量封顶)。 */\nfunction normalizeStringList(value: unknown, maxItems: number, maxLength: number): string[] {\n if (!Array.isArray(value)) return []\n const seen = new Set<string>()\n const items: string[] = []\n for (const item of value) {\n if (typeof item !== 'string') continue\n const trimmed = item.trim().slice(0, maxLength)\n if (trimmed === '' || seen.has(trimmed)) continue\n seen.add(trimmed)\n items.push(trimmed)\n if (items.length >= maxItems) break\n }\n return items\n}\n\n/** 归一化来源元数据(§14.1/14.2):仅保留字符串键值对;缺省/非法 → undefined。 */\nexport function normalizeMetadata(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined\n const metadata: Record<string, string> = {}\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry !== 'string' || key.trim() === '') continue\n metadata[key.trim().slice(0, 64)] = entry.slice(0, 2048)\n }\n return Object.keys(metadata).length > 0 ? metadata : undefined\n}\n\n/** 归一化一条评论(§9.5);结构非法返回 undefined。 */\nexport function normalizeComment(value: unknown): TaskComment | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as Record<string, unknown>\n if (typeof entry.id !== 'string' || entry.id === '') return undefined\n if (typeof entry.body !== 'string' || entry.body === '') return undefined\n if (!isFiniteNumber(entry.createdAt)) return undefined\n if (entry.author !== undefined && typeof entry.author !== 'string') return undefined\n if (entry.type !== undefined && !isCommentType(entry.type)) return undefined\n const comment: TaskComment = {\n id: entry.id,\n author: typeof entry.author === 'string' && entry.author.trim() !== '' ? entry.author.slice(0, 64) : 'user',\n body: entry.body.slice(0, 4000),\n type: isCommentType(entry.type) ? entry.type : 'user_feedback',\n createdAt: entry.createdAt,\n }\n return comment\n}\n\n/** 归一化评论集合(§9.5 comments,默认 []);非法条目丢弃。 */\nexport function normalizeComments(value: unknown): TaskComment[] {\n if (!Array.isArray(value)) return []\n const comments: TaskComment[] = []\n for (const entry of value) {\n const comment = normalizeComment(entry)\n if (comment !== undefined) comments.push(comment)\n }\n return comments\n}\n\n/** 归一化一条产物(§9.5 artifacts);结构非法返回 undefined。 */\nexport function normalizeArtifact(value: unknown): Artifact | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as Record<string, unknown>\n if (typeof entry.id !== 'string' || entry.id === '') return undefined\n if (!isArtifactType(entry.type)) return undefined\n if (typeof entry.title !== 'string' || entry.title === '') return undefined\n if (!isFiniteNumber(entry.createdAt)) return undefined\n if (entry.url !== undefined && typeof entry.url !== 'string') return undefined\n if (entry.contentRef !== undefined && typeof entry.contentRef !== 'string') return undefined\n const artifact: Artifact = {\n id: entry.id,\n type: entry.type,\n title: entry.title.slice(0, 500),\n createdAt: entry.createdAt,\n }\n if (typeof entry.url === 'string' && entry.url.trim() !== '') artifact.url = entry.url.slice(0, 2048)\n if (typeof entry.contentRef === 'string' && entry.contentRef.trim() !== '') artifact.contentRef = entry.contentRef.slice(0, 512)\n return artifact\n}\n\n/** 归一化产物集合(§9.5 artifacts,默认 []);非法条目丢弃。 */\nexport function normalizeArtifacts(value: unknown): Artifact[] {\n if (!Array.isArray(value)) return []\n const artifacts: Artifact[] = []\n for (const entry of value) {\n const artifact = normalizeArtifact(entry)\n if (artifact !== undefined) artifacts.push(artifact)\n }\n return artifacts\n}\n\n/** 归一化上下文快照(§9.5 context_snapshot);结构非法返回 undefined。 */\nexport function normalizeContextSnapshot(value: unknown): ContextSnapshot | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as Record<string, unknown>\n if (!isFiniteNumber(entry.updatedAt)) return undefined\n if (entry.goal !== undefined && typeof entry.goal !== 'string') return undefined\n if (entry.lastAiSummary !== undefined && typeof entry.lastAiSummary !== 'string') return undefined\n if (entry.latestUserFeedback !== undefined && typeof entry.latestUserFeedback !== 'string') return undefined\n const snapshot: ContextSnapshot = {\n keyDecisions: normalizeStringList(entry.keyDecisions, 50, 500),\n filePaths: normalizeStringList(entry.filePaths, 100, 500),\n relatedLinks: normalizeStringList(entry.relatedLinks, 20, 2048),\n updatedAt: entry.updatedAt,\n }\n if (typeof entry.goal === 'string' && entry.goal.trim() !== '') snapshot.goal = entry.goal.trim().slice(0, 2000)\n if (typeof entry.lastAiSummary === 'string' && entry.lastAiSummary.trim() !== '') {\n snapshot.lastAiSummary = entry.lastAiSummary.trim().slice(0, 8000)\n }\n if (typeof entry.latestUserFeedback === 'string' && entry.latestUserFeedback.trim() !== '') {\n snapshot.latestUserFeedback = entry.latestUserFeedback.trim().slice(0, 4000)\n }\n return snapshot\n}\n\n/** 归一化一条自动化规则(§9.5 automation_rules);结构非法返回 undefined。 */\nexport function normalizeAutomationRule(value: unknown): AutomationRule | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const rule = value as Record<string, unknown>\n if (typeof rule.id !== 'string' || rule.id === '') return undefined\n if (!isAutomationSource(rule.source)) return undefined\n if (typeof rule.enabled !== 'boolean') return undefined\n if (typeof rule.createdAt !== 'number' || !Number.isFinite(rule.createdAt)) return undefined\n if (typeof rule.updatedAt !== 'number' || !Number.isFinite(rule.updatedAt)) return undefined\n const trigger = rule.trigger\n if (typeof trigger !== 'object' || trigger === null) return undefined\n const triggerRaw = trigger as Record<string, unknown>\n if (triggerRaw.kind !== 'cron' || typeof triggerRaw.cron !== 'string') return undefined\n if (!isPlausibleCron(triggerRaw.cron)) return undefined\n const config: Record<string, string> = {}\n if (rule.config !== undefined) {\n if (typeof rule.config !== 'object' || rule.config === null || Array.isArray(rule.config)) return undefined\n for (const [key, entry] of Object.entries(rule.config as Record<string, unknown>)) {\n if (typeof entry !== 'string' || key.trim() === '') continue\n config[key.trim().slice(0, 64)] = entry.slice(0, 2048)\n }\n }\n let labels: string[] = []\n if (rule.filter !== undefined) {\n if (typeof rule.filter !== 'object' || rule.filter === null) return undefined\n const filter = rule.filter as Record<string, unknown>\n if (!Array.isArray(filter.labels) || !filter.labels.every(label => typeof label === 'string')) return undefined\n labels = normalizeTags(filter.labels)\n }\n const automation: AutomationRule = {\n id: rule.id,\n enabled: rule.enabled,\n source: rule.source,\n trigger: { kind: 'cron', cron: triggerRaw.cron },\n config,\n filter: { labels },\n createdAt: rule.createdAt,\n updatedAt: rule.updatedAt,\n }\n if (isFiniteNumber(triggerRaw.nextRunAt)) automation.trigger.nextRunAt = triggerRaw.nextRunAt\n if (isFiniteNumber(triggerRaw.lastTriggeredAt)) automation.trigger.lastTriggeredAt = triggerRaw.lastTriggeredAt\n return automation\n}\n\n/** 归一化自动化规则集合(§9.5 automation_rules,默认 []);非法条目丢弃。 */\nexport function normalizeAutomationRules(value: unknown): AutomationRule[] {\n if (!Array.isArray(value)) return []\n const rules: AutomationRule[] = []\n for (const entry of value) {\n const rule = normalizeAutomationRule(entry)\n if (rule !== undefined) rules.push(rule)\n }\n return rules\n}\n\n/**\n * 归一化一条任务行(§9.2)。\n *\n * 结构非法(id/title/description/prompt/createdAt/updatedAt/executions 任一不\n * 符合)→ 返回 undefined(整行丢弃,HostLedger 记入 scheduler.error);\n * 语义非法 → 就地修复:\n * - 未知状态 → `todo`(未来版本的未知状态落入待办而非丢行,对齐参考实现);\n * - 未知 source/permission → undefined;空白 workspaceId/mode/project/parentId\n * → undefined;archivedAt 非有限数 → undefined;\n * - tags 非字符串数组 → [];order 非有限数 → 0(T009 重算列内唯一);\n * - schedule 交给 normalizeSchedule(修复或丢弃,不丢行)。\n */\nexport function normalizeTask(value: unknown): TaskRecord | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const record = value as Record<string, unknown>\n if (typeof record.id !== 'string' || record.id === '') return undefined\n if (typeof record.title !== 'string') return undefined\n if (typeof record.description !== 'string') return undefined\n if (typeof record.prompt !== 'string') return undefined\n if (!isFiniteNumber(record.createdAt) || !isFiniteNumber(record.updatedAt)) return undefined\n if (!Array.isArray(record.executions)) return undefined\n const executions: ExecutionRecord[] = []\n for (const entry of record.executions) {\n const execution = normalizeExecution(entry)\n if (execution === undefined) return undefined\n executions.push(execution)\n }\n const task: TaskRecord = {\n id: record.id,\n title: record.title,\n description: record.description,\n prompt: record.prompt,\n status: isTaskStatus(record.status) ? record.status : 'todo',\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n executions,\n tags: normalizeTags(record.tags),\n comments: normalizeComments(record.comments),\n artifacts: normalizeArtifacts(record.artifacts),\n order: isFiniteNumber(record.order) ? record.order : 0,\n }\n if (isTaskSource(record.source)) task.source = record.source\n const metadata = normalizeMetadata(record.metadata)\n if (metadata !== undefined) task.metadata = metadata\n const contextSnapshot = normalizeContextSnapshot(record.contextSnapshot)\n if (contextSnapshot !== undefined) task.contextSnapshot = contextSnapshot\n const schedule = normalizeSchedule(record.schedule)\n if (schedule !== undefined) task.schedule = schedule\n const workspaceId = normalizeOptionalString(record.workspaceId)\n if (workspaceId !== undefined) task.workspaceId = workspaceId\n const mode = normalizeOptionalString(record.mode)\n if (mode !== undefined) task.mode = mode\n if (isTaskPermission(record.permission)) task.permission = record.permission\n if (isFiniteNumber(record.archivedAt)) task.archivedAt = record.archivedAt\n const project = normalizeOptionalString(record.project)\n if (project !== undefined) task.project = project\n const parentId = normalizeOptionalString(record.parentId)\n if (parentId !== undefined) task.parentId = parentId\n return task\n}\n\n/** Host 本地时区(§9.1 scheduler.timeZone)。 */\nexport function hostTimeZone(): string {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'local'\n}\n\n/**\n * 空账本文档(新账本/损坏隔离后的重建起点)。\n * @param error - 首次持久化即可见的错误(如损坏隔离说明)。\n */\nexport function emptyLedgerDocument(error?: string): LedgerDocument {\n return {\n schemaVersion: LEDGER_SCHEMA_VERSION,\n revision: 0,\n tasks: [],\n scheduler: {\n timeZone: hostTimeZone(),\n ledgerId: crypto.randomUUID(),\n ...(error === undefined ? {} : { error }),\n },\n recentRequests: [],\n automationRules: [],\n }\n}\n\n/**\n * 解析并归一化账本文档(§9.1)。\n *\n * 账本级损坏(非 JSON、根非对象、schemaVersion ≠ 2、tasks 非数组)抛\n * `LedgerSchemaError`,由 HostLedger 隔离原文件(`corrupt-*` 保留原字节)后\n * 以空账本重建。行级损坏(非法任务行、非法执行记录行)丢弃该行并记入\n * `scheduler.error`;`revision` 非负安全整数 → 0;`recentRequests` 仅保留\n * 合法条目并截断到最近 256 条。\n */\nexport function parseLedgerDocument(raw: string): LedgerDocument {\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new LedgerSchemaError(`ledger is not valid JSON: ${error instanceof Error ? error.message : String(error)}`)\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new LedgerSchemaError('ledger root must be a JSON object')\n }\n const doc = parsed as Record<string, unknown>\n if (doc.schemaVersion !== LEDGER_SCHEMA_VERSION) {\n throw new LedgerSchemaError(\n `unsupported ledger schemaVersion ${JSON.stringify(doc.schemaVersion)} (expected ${LEDGER_SCHEMA_VERSION})`,\n )\n }\n if (!Array.isArray(doc.tasks)) throw new LedgerSchemaError('ledger tasks must be an array')\n\n const dropped: string[] = []\n const tasks: TaskRecord[] = []\n for (const row of doc.tasks) {\n const task = normalizeTask(row)\n if (task === undefined) {\n const id = typeof row === 'object' && row !== null ? (row as { id?: unknown }).id : undefined\n dropped.push(typeof id === 'string' ? id : '<invalid row>')\n continue\n }\n tasks.push(task)\n }\n\n const schedulerRaw = typeof doc.scheduler === 'object' && doc.scheduler !== null\n ? doc.scheduler as Record<string, unknown>\n : {}\n const scheduler: LedgerScheduler = {\n timeZone: hostTimeZone(),\n ledgerId: typeof schedulerRaw.ledgerId === 'string' && schedulerRaw.ledgerId !== ''\n ? schedulerRaw.ledgerId\n : crypto.randomUUID(),\n }\n if (isFiniteNumber(schedulerRaw.lastTickAt)) scheduler.lastTickAt = schedulerRaw.lastTickAt\n if (typeof schedulerRaw.error === 'string') scheduler.error = schedulerRaw.error\n if (Array.isArray(schedulerRaw.importedSources)) {\n scheduler.importedSources = schedulerRaw.importedSources.filter((source): source is string => typeof source === 'string')\n }\n if (dropped.length > 0) scheduler.error = `dropped ${dropped.length} invalid task row(s): ${dropped.join(', ')}`\n\n const droppedRules: string[] = []\n const automationRules: AutomationRule[] = []\n if (doc.automationRules !== undefined && !Array.isArray(doc.automationRules)) {\n droppedRules.push('<invalid rules list>')\n } else if (Array.isArray(doc.automationRules)) {\n for (const row of doc.automationRules) {\n const rule = normalizeAutomationRule(row)\n if (rule === undefined) {\n const id = typeof row === 'object' && row !== null ? (row as { id?: unknown }).id : undefined\n droppedRules.push(typeof id === 'string' ? id : '<invalid rule>')\n continue\n }\n automationRules.push(rule)\n }\n }\n if (droppedRules.length > 0) {\n scheduler.error = `dropped ${droppedRules.length} invalid automation rule(s): ${droppedRules.join(', ')}`\n }\n\n return {\n schemaVersion: LEDGER_SCHEMA_VERSION,\n revision: Number.isSafeInteger(doc.revision) && (doc.revision as number) >= 0 ? doc.revision as number : 0,\n tasks,\n scheduler,\n recentRequests: Array.isArray(doc.recentRequests)\n ? doc.recentRequests.flatMap((entry) => {\n if (typeof entry !== 'object' || entry === null) return []\n const request = entry as { requestId?: unknown; fingerprint?: unknown }\n return typeof request.requestId === 'string' && request.requestId !== ''\n && typeof request.fingerprint === 'string'\n ? [{ requestId: request.requestId, fingerprint: request.fingerprint }]\n : []\n }).slice(-MAX_REQUEST_CACHE)\n : [],\n automationRules,\n }\n}\n","/**\n * 通用确认对话框:危险操作(删除任务)的二次确认。\n */\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\n\n/** Confirm overlay props. */\nexport interface ConfirmDialogProps {\n title: string\n message: string\n confirmLabel: string\n /** Render the confirm button in the danger style. */\n danger?: boolean\n onCancel: () => void\n onConfirm: () => void\n}\n\n/** Small confirm overlay. */\nexport function ConfirmDialog({ title, message, confirmLabel, danger, onCancel, onConfirm }: ConfirmDialogProps) {\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onCancel() }}>\n <div className={css.modal} role=\"alertdialog\" aria-label={title}>\n <h2 className={css.modalTitle}>{title}</h2>\n <p className={css.confirmMessage}>{message}</p>\n <footer className={css.modalFooter}>\n <button type=\"button\" className={css.ghostButton} onClick={onCancel}>\n {t('delete.cancel')}\n </button>\n <button\n type=\"button\"\n className={danger ? css.dangerButton : css.primaryButton}\n onClick={onConfirm}\n >\n {confirmLabel}\n </button>\n </footer>\n </div>\n </div>\n )\n}\n","/**\n * 确认对话框(T011 §12.7/§14.4):候选任务(proposed)的确认/拒绝入口。\n *\n * - 可编辑标题/描述/prompt(编辑后确认 = update 语义,经 confirm 前先 update);\n * - 选择落列:待规划(backlog)/ 待办(todo)——confirm 目标仅这两列(§10.2);\n * - 拒绝 = dismiss(删除候选,二次确认);取消 = 关闭不改动;\n * - 来源会话行:候选来自对话(source=conversation)时展示来源会话并可跳回\n * 对话(controller.openSession,会话切换自动关闭看板);\n * - 经 Host 确认(confirm action 返回新快照、候选离开 proposed 列)后才关闭;\n * 失败展示错误并可重试(UI 不先写未确认状态)。\n */\nimport { useState } from 'react'\nimport { SOURCE_CONVERSATION_META_KEY } from '../../core/model.ts'\nimport type { TaskRecord } from '../../core/model.ts'\nimport type { BoardController } from '../board-controller.ts'\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\nimport { ConfirmDialog } from './ConfirmDialog.tsx'\n\n/** 确认目标列(§10.2/§11.2:仅 backlog | todo)。 */\nconst CONFIRM_TARGETS: readonly { target: 'backlog' | 'todo'; label: string }[] = [\n { target: 'backlog', label: t('confirm.toBacklog') },\n { target: 'todo', label: t('confirm.toTodo') },\n]\n\n/** 确认对话框浮层。 */\nexport function ConfirmTaskDialog({ controller, task, onClose }: {\n controller: BoardController\n task: TaskRecord\n onClose: () => void\n}) {\n const [title, setTitle] = useState(task.title)\n const [description, setDescription] = useState(task.description)\n const [prompt, setPrompt] = useState(task.prompt)\n const [target, setTarget] = useState<'backlog' | 'todo'>('todo')\n const [error, setError] = useState<string | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const [confirmDismiss, setConfirmDismiss] = useState(false)\n const sourceConversationId = task.metadata?.[SOURCE_CONVERSATION_META_KEY]\n\n const submit = async (): Promise<void> => {\n if (title.trim() === '') {\n setError(t('new.required'))\n return\n }\n setError(undefined)\n setPending(true)\n try {\n // 编辑后的候选先 update 落账(§10.2:proposed 可编辑),再 confirm 落列。\n if (title !== task.title || description !== task.description || prompt !== task.prompt) {\n controller.updateTask(task.id, { title, description, prompt })\n }\n const confirmed = await controller.confirmTask(task.id, target)\n if (!confirmed) {\n setError(controller.getSnapshot().transportError ?? t('board.hostError', { error: 'confirm failed' }))\n return\n }\n onClose()\n } finally {\n setPending(false)\n }\n }\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onClose() }}>\n <form\n className={css.modal}\n role=\"dialog\"\n aria-label={t('confirm.title')}\n onSubmit={event => { event.preventDefault(); void submit() }}\n >\n <h2 className={css.modalTitle}>{t('confirm.title')}</h2>\n <p className={css.detailText}>{t('confirm.subtitle')}</p>\n\n {sourceConversationId !== undefined && (\n <div className={css.sourceRow} data-dsh-part=\"source-row\">\n <span>{t('detail.sourceConversation')}:</span>\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { controller.openSession(sourceConversationId) }}\n >\n {t('detail.jumpToConversation')} ⌁\n </button>\n </div>\n )}\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.title')}</span>\n <input\n className={css.input}\n value={title}\n autoFocus\n placeholder={t('new.titlePlaceholder')}\n onChange={event => { setTitle(event.target.value); setError(undefined) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.description')}</span>\n <textarea\n className={css.input}\n rows={3}\n value={description}\n placeholder={t('new.descriptionPlaceholder')}\n onChange={event => { setDescription(event.target.value) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.prompt')}</span>\n <textarea\n className={css.input}\n rows={4}\n value={prompt}\n placeholder={t('new.promptPlaceholder')}\n onChange={event => { setPrompt(event.target.value) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('confirm.target')}</span>\n <div className={css.confirmTargetRow} role=\"radiogroup\" aria-label={t('confirm.target')}>\n {CONFIRM_TARGETS.map(option => (\n <button\n key={option.target}\n type=\"button\"\n role=\"radio\"\n aria-checked={target === option.target}\n className={`${css.confirmTargetButton}${target === option.target ? ` ${css.confirmTargetActive}` : ''}`}\n onClick={() => { setTarget(option.target) }}\n >\n {option.label}\n </button>\n ))}\n </div>\n </label>\n\n {error !== undefined && <p className={css.formError} role=\"alert\">{error}</p>}\n\n <footer className={css.modalFooter}>\n <button\n type=\"button\"\n className={css.dangerGhostButton}\n disabled={pending}\n onClick={() => { setConfirmDismiss(true) }}\n >\n {t('confirm.dismiss')}\n </button>\n <button type=\"button\" className={css.ghostButton} onClick={onClose}>\n {t('new.cancel')}\n </button>\n <button type=\"submit\" className={css.primaryButton} disabled={pending}>\n {pending ? t('board.pending') : t('confirm.submit')}\n </button>\n </footer>\n </form>\n\n {confirmDismiss && (\n <ConfirmDialog\n title={t('confirm.dismissTitle')}\n message={t('confirm.dismissMessage', { name: task.title })}\n confirmLabel={t('confirm.dismiss')}\n danger\n onCancel={() => { setConfirmDismiss(false) }}\n onConfirm={() => {\n setConfirmDismiss(false)\n controller.dismissTask(task.id)\n onClose()\n }}\n />\n )}\n </div>\n )\n}\n","/**\n * 标签编辑器(T008 §12.8):chips 展示 + 逐个删除 + 输入添加,datalist 提供\n * 已有标签建议。任务详情页与新建任务表单共用;变更经 `onChange` 回传\n * (提交动作由父组件负责:详情页经 update patch、新建表单随 create input 落账)。\n *\n * - 输入为空/重复标签忽略(大小写敏感,与 normalizeTags 语义一致);\n * - `disabled`(pending)时禁删禁添;\n * - datalist id 用 useId 生成,避免同页多个实例(详情 + 新建并存)冲突。\n */\nimport { useId, useRef, useState } from 'react'\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\n\nexport function TagEditor({ tags, suggestions, disabled, onChange }: {\n tags: readonly string[]\n suggestions: readonly string[]\n disabled: boolean\n onChange: (tags: string[]) => void\n}) {\n const listId = useId()\n const [draft, setDraft] = useState('')\n const inputRef = useRef<HTMLInputElement>(null)\n\n const commit = (): void => {\n const tag = draft.trim()\n setDraft('')\n if (tag === '' || tags.includes(tag)) return\n onChange([...tags, tag])\n }\n\n return (\n <div className={css.tagEditor}>\n {tags.length > 0 && (\n <span className={css.filterChips}>\n {tags.map(tag => (\n <span key={tag} className={`${css.filterChip} ${css.tagEditorChip}`}>\n {tag}\n <button\n type=\"button\"\n className={css.tagEditorRemove}\n aria-label={t('filter.removeTag', { tag })}\n disabled={disabled}\n onClick={() => { onChange(tags.filter(candidate => candidate !== tag)) }}\n >\n ×\n </button>\n </span>\n ))}\n </span>\n )}\n <input\n ref={inputRef}\n className={css.input}\n list={listId}\n value={draft}\n disabled={disabled}\n placeholder={t('new.tagsPlaceholder')}\n aria-label={t('filter.tags')}\n onChange={event => { setDraft(event.target.value) }}\n onBlur={commit}\n onKeyDown={event => {\n if (event.key === 'Enter') {\n event.preventDefault()\n commit()\n }\n }}\n />\n <datalist id={listId}>\n {suggestions.filter(tag => !tags.includes(tag)).map(tag => (\n <option key={tag} value={tag} />\n ))}\n </datalist>\n </div>\n )\n}\n","/**\n * 新建任务(T004 §12.4 + T008 §12.8):标题(必填)/描述/执行 Prompt(留空用\n * 标题)/可选项目与标签/工作区/模式/权限。\n *\n * - 经 Host 确认(action create 返回新快照、任务可见)后才关闭弹窗;失败展示\n * 错误并可重试——UI 不先写未确认状态;\n * - 可选执行安排(cron/one-shot 编辑器)归 T006/T007,本期表单不含;\n * - 项目/标签建议由当前账本任务推导(distinctProjects/distinctTags),随\n * 控制器快照刷新;提交随 create input 落账(transitions 层 normalize)。\n */\nimport { useEffect, useId, useState } from 'react'\nimport type { TaskPermission } from '../../core/model.ts'\nimport { TASK_PERMISSIONS } from '../../core/model.ts'\nimport type { BoardController } from '../board-controller.ts'\nimport { distinctProjects, distinctTags } from '../filter.ts'\nimport { t, type NovaTaskBoardKey } from '../locales.ts'\nimport css from '../board.module.css'\nimport { TagEditor } from './TagEditor.tsx'\n\n/** 新建任务表单浮层。 */\nexport function NewTaskModal({ controller, onClose }: { controller: BoardController; onClose: () => void }) {\n const [title, setTitle] = useState('')\n const [description, setDescription] = useState('')\n const [prompt, setPrompt] = useState('')\n const [workspaceId, setWorkspaceId] = useState('')\n const [mode, setMode] = useState('')\n const [permission, setPermission] = useState('')\n const [project, setProject] = useState('')\n const [tags, setTags] = useState<string[]>([])\n const [error, setError] = useState<string | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const [options, setOptions] = useState(controller.getSnapshot().executionOptions)\n const projectListId = useId()\n const all = controller.getSnapshot().tasks\n\n // 工作区列表与预设名册来自运行时,跟随其到达/更新。\n useEffect(\n () => controller.subscribe(() => setOptions(controller.getSnapshot().executionOptions)),\n [controller],\n )\n\n const submit = async (): Promise<void> => {\n if (title.trim() === '') {\n setError(t('new.required'))\n return\n }\n setError(undefined)\n setPending(true)\n try {\n const task = await controller.createTaskConfirmed({\n title,\n description,\n prompt,\n workspaceId: workspaceId === '' ? undefined : workspaceId,\n mode: mode === '' ? undefined : mode,\n permission: permission === '' ? undefined : permission as TaskPermission,\n project: project.trim() === '' ? undefined : project,\n tags,\n })\n if (task === undefined) {\n setError(controller.getSnapshot().transportError ?? t('new.required'))\n return\n }\n onClose()\n } finally {\n setPending(false)\n }\n }\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onClose() }}>\n <form\n className={css.modal}\n role=\"dialog\"\n aria-label={t('board.new')}\n onSubmit={event => { event.preventDefault(); void submit() }}\n >\n <h2 className={css.modalTitle}>{t('board.new')}</h2>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.title')}</span>\n <input\n className={css.input}\n value={title}\n autoFocus\n placeholder={t('new.titlePlaceholder')}\n onChange={event => { setTitle(event.target.value); setError(undefined) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.description')}</span>\n <textarea\n className={css.input}\n rows={3}\n value={description}\n placeholder={t('new.descriptionPlaceholder')}\n onChange={event => { setDescription(event.target.value) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.prompt')}</span>\n <textarea\n className={css.input}\n rows={4}\n value={prompt}\n placeholder={t('new.promptPlaceholder')}\n onChange={event => { setPrompt(event.target.value) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.project')}</span>\n <input\n className={css.input}\n list={projectListId}\n value={project}\n placeholder={t('detail.projectPlaceholder')}\n onChange={event => { setProject(event.target.value) }}\n />\n <datalist id={projectListId}>\n {distinctProjects(all).filter(name => name !== project.trim()).map(name => (\n <option key={name} value={name} />\n ))}\n </datalist>\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.tags')}</span>\n <TagEditor\n tags={tags}\n suggestions={distinctTags(all)}\n disabled={pending}\n onChange={setTags}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.workspace')}</span>\n <select\n className={css.select}\n value={workspaceId}\n onChange={event => { setWorkspaceId(event.target.value) }}\n >\n <option value=\"\">{t('exec.workspace.recent')}</option>\n {options.workspaces.map(workspace => (\n <option key={workspace.workspaceId} value={workspace.workspaceId}>{workspace.title}</option>\n ))}\n </select>\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.mode')}</span>\n <select\n className={css.select}\n value={mode}\n onChange={event => { setMode(event.target.value) }}\n >\n <option value=\"\">{t('exec.mode.default')}</option>\n {options.presets.map(preset => (\n <option key={preset.id} value={preset.id} disabled={preset.broken !== undefined}>\n {preset.name ?? preset.id}\n {preset.isDefault ? t('exec.mode.defaultSuffix') : ''}\n {preset.broken !== undefined ? t('exec.mode.brokenSuffix') : ''}\n </option>\n ))}\n </select>\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.permission')}</span>\n <select\n className={css.select}\n value={permission}\n onChange={event => { setPermission(event.target.value) }}\n >\n <option value=\"\">{t('exec.permission.default')}</option>\n {TASK_PERMISSIONS.map(id => (\n <option key={id} value={id}>{t(`exec.permission.${id}` as NovaTaskBoardKey)}</option>\n ))}\n </select>\n </label>\n\n {error !== undefined && <p className={css.formError} role=\"alert\">{error}</p>}\n\n <footer className={css.modalFooter}>\n <button type=\"button\" className={css.ghostButton} onClick={onClose}>\n {t('new.cancel')}\n </button>\n <button type=\"submit\" className={css.primaryButton} disabled={pending}>\n {pending ? t('board.pending') : t('new.submit')}\n </button>\n </footer>\n </form>\n </div>\n )\n}\n","/**\n * 需求拆分对话框(T012 §12.9/§14.5):看板「从需求拆分」入口(粘贴需求文本或\n * 填写工作区文件路径)与任务详情「递归拆分」入口共用。\n *\n * - 提交 `start-split`:Host 启动独立拆分会话(复用执行通道,B8),agent 按\n * §14.5 方法论(粒度启发式 + 拆后自检)拆分;结算后父需求任务 + 批量候选\n * 进入「待确认」列(source=requirement,人工确认闸门复用 T011——confirm 仍是\n * 唯一 promote 路径)。受理成功后关闭;拆分异步进行,SSE revision 变化时\n * 批量候选到达待确认列(对话框内提示)。\n * - 递归拆分(§12.9):从任务详情发起时 prefill 标题并携带 parentTaskId——\n * 新建的父需求任务「需求:<标题>」挂到该任务下(parentId),子任务再指向\n * 新父任务,形成树状层级。\n * - 项目/标签为默认归属(父任务与子任务自动带,§14.5 子任务产出结构);标签\n * 建议由当前账本任务推导(distinctTags),随控制器快照刷新。\n */\nimport { useId, useState } from 'react'\nimport type { BoardController } from '../board-controller.ts'\nimport { distinctProjects, distinctTags } from '../filter.ts'\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\nimport { TagEditor } from './TagEditor.tsx'\n\n/** 需求来源输入模式(§12.9:粘贴文本,或工作区文件路径)。 */\ntype SourceMode = 'text' | 'file'\n\n/** 递归拆分的预填(从任务详情发起;§12.9「对任一任务可再次发起拆分」)。 */\nexport interface RequirementSplitPrefill {\n /** 需求标题(父任务「需求:<标题>」)。 */\n title?: string\n /** 递归拆分:新父需求任务挂到该任务下。 */\n parentTaskId?: string\n}\n\n/** 需求拆分对话框浮层。 */\nexport function RequirementSplitModal({ controller, prefill, onClose }: {\n controller: BoardController\n prefill?: RequirementSplitPrefill\n onClose: () => void\n}) {\n const [title, setTitle] = useState(prefill?.title ?? '')\n const [project, setProject] = useState('')\n const [tags, setTags] = useState<string[]>([])\n const [mode, setMode] = useState<SourceMode>('text')\n const [text, setText] = useState('')\n const [filePath, setFilePath] = useState('')\n const [error, setError] = useState<string | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const projectListId = useId()\n const all = controller.getSnapshot().tasks\n\n const submit = async (): Promise<void> => {\n if (title.trim() === '' || (mode === 'text' ? text.trim() === '' : filePath.trim() === '')) {\n setError(t('split.required'))\n return\n }\n setError(undefined)\n setPending(true)\n try {\n const accepted = await controller.startRequirementSplit({\n title,\n ...(mode === 'text' ? { text } : { filePath }),\n ...(project.trim() === '' ? {} : { project }),\n ...(tags.length > 0 ? { tags } : {}),\n ...(prefill?.parentTaskId === undefined ? {} : { parentTaskId: prefill.parentTaskId }),\n })\n if (!accepted) {\n setError(controller.getSnapshot().transportError ?? t('split.required'))\n return\n }\n onClose()\n } finally {\n setPending(false)\n }\n }\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onClose() }}>\n <form\n className={css.modal}\n role=\"dialog\"\n aria-label={t('split.entry')}\n onSubmit={event => { event.preventDefault(); void submit() }}\n >\n <h2 className={css.modalTitle}>{prefill?.parentTaskId !== undefined ? t('split.recursiveTitle') : t('split.entry')}</h2>\n <p className={css.detailText}>\n {prefill?.parentTaskId !== undefined ? t('split.recursiveHint') : t('split.subtitle')}\n </p>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('split.requirementTitle')}</span>\n <input\n className={css.input}\n value={title}\n autoFocus\n placeholder={t('split.requirementTitlePlaceholder')}\n onChange={event => { setTitle(event.target.value); setError(undefined) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.project')}</span>\n <input\n className={css.input}\n list={projectListId}\n value={project}\n placeholder={t('detail.projectPlaceholder')}\n onChange={event => { setProject(event.target.value) }}\n />\n <datalist id={projectListId}>\n {distinctProjects(all).filter(name => name !== project.trim()).map(name => (\n <option key={name} value={name} />\n ))}\n </datalist>\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.tags')}</span>\n <TagEditor\n tags={tags}\n suggestions={distinctTags(all)}\n disabled={pending}\n onChange={setTags}\n />\n </label>\n\n <div className={css.field} role=\"radiogroup\" aria-label={t('split.source')}>\n <span className={css.fieldLabel}>{t('split.source')}</span>\n <div className={css.confirmTargetRow}>\n {([['text', t('split.sourceText')], ['file', t('split.sourceFile')]] as const).map(([value, label]) => (\n <button\n key={value}\n type=\"button\"\n role=\"radio\"\n aria-checked={mode === value}\n className={`${css.confirmTargetButton}${mode === value ? ` ${css.confirmTargetActive}` : ''}`}\n onClick={() => { setMode(value); setError(undefined) }}\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n\n {mode === 'text' ? (\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('split.text')}</span>\n <textarea\n className={css.input}\n rows={8}\n value={text}\n placeholder={t('split.textPlaceholder')}\n onChange={event => { setText(event.target.value); setError(undefined) }}\n />\n </label>\n ) : (\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('split.file')}</span>\n <input\n className={css.input}\n value={filePath}\n placeholder={t('split.filePlaceholder')}\n onChange={event => { setFilePath(event.target.value); setError(undefined) }}\n />\n </label>\n )}\n\n {error !== undefined && <p className={css.formError} role=\"alert\">{error}</p>}\n\n <footer className={css.modalFooter}>\n <span className={css.detailMeta}>{t('split.asyncHint')}</span>\n <button type=\"button\" className={css.ghostButton} onClick={onClose} disabled={pending}>\n {t('new.cancel')}\n </button>\n <button type=\"submit\" className={css.primaryButton} disabled={pending}>\n {pending ? t('board.pending') : t('split.submit')}\n </button>\n </footer>\n </form>\n </div>\n )\n}\n","/**\n * 任务状态 → 文案键(列标题与详情徽标)。\n * proposed(待确认)属 T011;本表为其预留键,T004 的五列不渲染该列。\n */\nimport type { TaskStatus } from '../../core/model.ts'\nimport type { NovaTaskBoardKey } from '../locales.ts'\n\n/** Task status → locale key. */\nexport const STATUS_KEY: Record<TaskStatus, NovaTaskBoardKey> = {\n proposed: 'board.status.proposed',\n backlog: 'board.status.backlog',\n todo: 'board.status.todo',\n running: 'board.status.running',\n done: 'board.status.done',\n failed: 'board.status.failed',\n}\n","/**\n * 批量确认对话框(T012 §12.9):拆分结果(proposed 候选)的批量处理——\n * 逐条编辑(标题/描述/prompt/项目/标签)、增删条目、批量确认到待规划/待办、\n * 整体或逐条拒绝(§12.9「确认对话框支持逐条编辑、增删条目、批量确认、整体或\n * 逐条拒绝」)。\n *\n * - 行 = 「待确认」列中的 proposed 候选(含父需求任务「需求:<标题>」);\n * children 行展示所属需求(父任务)引用(可跳转);\n * - **确认全部**:逐行「编辑过的既有候选先 update;新条目先 propose;全部经\n * confirm 到所选列」——confirm 仍是唯一 promote 路径(T011 闸门);任一失败\n * 留在对话框并展示错误(UI 不先写未确认状态);\n * - 逐条移除 = dismiss(proposed 专属路径,二次确认);整体拒绝 = 全部 dismiss\n * (二次确认);\n * - 「添加条目」追加空行:确认时先 propose 再 confirm(进入同一目标列)。\n */\nimport { useId, useState } from 'react'\nimport type { TaskRecord } from '../../core/model.ts'\nimport type { BoardController } from '../board-controller.ts'\nimport { distinctProjects, distinctTags } from '../filter.ts'\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\nimport { ConfirmDialog } from './ConfirmDialog.tsx'\nimport { STATUS_KEY } from './status-key.ts'\nimport { TagEditor } from './TagEditor.tsx'\n\n/** 一行候选(既有任务或新增空行;编辑只改本地草稿,提交时经协议落账)。 */\ninterface ReviewRow {\n key: string\n task?: TaskRecord\n title: string\n description: string\n prompt: string\n project: string\n tags: string[]\n}\n\n/** 新增行 key 序列(同会话内稳定唯一)。 */\nlet newRowSeq = 0\n\n/** 确认目标列(§10.2/§11.2:仅 backlog | todo)。 */\nconst BATCH_TARGETS: readonly { target: 'backlog' | 'todo'; label: string }[] = [\n { target: 'backlog', label: t('confirm.toBacklog') },\n { target: 'todo', label: t('confirm.toTodo') },\n]\n\n/** 批量确认对话框浮层。 */\nexport function SplitConfirmDialog({ controller, tasks, onClose }: {\n controller: BoardController\n /** 待确认列中的候选任务(含父需求任务;快照传入,行编辑不影响账本)。 */\n tasks: readonly TaskRecord[]\n onClose: () => void\n}) {\n const all = controller.getSnapshot().tasks\n const [rows, setRows] = useState<ReviewRow[]>(() => tasks.map(task => ({\n key: task.id,\n task,\n title: task.title,\n description: task.description,\n prompt: task.prompt,\n project: task.project ?? '',\n tags: [...task.tags],\n })))\n const [target, setTarget] = useState<'backlog' | 'todo'>('todo')\n const [error, setError] = useState<string | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const [confirmDismissAll, setConfirmDismissAll] = useState(false)\n const [removing, setRemoving] = useState<string | undefined>(undefined)\n const projectListId = useId()\n\n const patchRow = (key: string, patch: Partial<ReviewRow>): void => {\n setRows(current => current.map(row => row.key === key ? { ...row, ...patch } : row))\n }\n\n const addRow = (): void => {\n const key = `new-${Date.now().toString(36)}-${newRowSeq}`\n newRowSeq += 1\n setRows(current => [...current, { key, title: '', description: '', prompt: '', project: '', tags: [] }])\n }\n\n /** 移除一行:既有候选 → dismiss(二次确认);新增空行 → 直接丢弃。 */\n const removeRow = (key: string): void => {\n const row = rows.find(candidate => candidate.key === key)\n if (row === undefined) return\n if (row.task !== undefined) {\n setRemoving(key)\n return\n }\n setRows(current => current.filter(candidate => candidate.key !== key))\n }\n\n /** 确认全部到所选列(逐行:编辑 → update、新条目 → propose、全部 → confirm)。 */\n const confirmAll = async (): Promise<void> => {\n for (const row of rows) {\n if (row.title.trim() === '') {\n setError(t('new.required'))\n return\n }\n }\n setError(undefined)\n setPending(true)\n try {\n for (const row of rows) {\n let task = row.task\n if (task === undefined) {\n // 新增条目:先 propose 再 confirm(进入同一目标列)。\n task = await controller.proposeTask({\n title: row.title,\n description: row.description,\n prompt: row.prompt,\n ...(row.project.trim() === '' ? {} : { project: row.project }),\n tags: row.tags,\n })\n if (task === undefined) {\n setError(controller.getSnapshot().transportError ?? t('new.required'))\n return\n }\n } else if (row.title !== task.title || row.description !== task.description\n || row.prompt !== task.prompt || row.project !== (task.project ?? '')\n || JSON.stringify(row.tags) !== JSON.stringify(task.tags)) {\n controller.updateTask(task.id, {\n title: row.title,\n description: row.description,\n prompt: row.prompt,\n ...(row.project.trim() === '' ? { project: undefined } : { project: row.project }),\n tags: row.tags,\n })\n }\n const confirmed = await controller.confirmTask(task.id, target)\n if (!confirmed) {\n setError(controller.getSnapshot().transportError ?? t('board.hostError', { error: 'confirm failed' }))\n return\n }\n }\n onClose()\n } finally {\n setPending(false)\n }\n }\n\n const dismissAll = (): void => {\n setConfirmDismissAll(false)\n for (const row of rows) {\n if (row.task !== undefined) controller.dismissTask(row.task.id)\n }\n onClose()\n }\n\n const parentTitleOf = (row: ReviewRow): { id: string; title: string } | undefined => {\n const parentId = row.task?.parentId\n if (parentId === undefined) return undefined\n const parent = all.find(candidate => candidate.id === parentId)\n return parent === undefined ? undefined : { id: parentId, title: parent.title }\n }\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onClose() }}>\n <div className={css.modal} role=\"dialog\" aria-label={t('split.batchTitle')}>\n <h2 className={css.modalTitle}>{t('split.batchTitle')}</h2>\n <p className={css.detailText}>{t('split.batchSubtitle')}</p>\n\n {rows.length === 0 ? (\n <p className={css.detailText}>{t('split.empty')}</p>\n ) : (\n <div className={css.splitRows} data-dsh-part=\"split-rows\">\n {rows.map(row => {\n const parent = parentTitleOf(row)\n return (\n <div key={row.key} className={css.splitRow} data-dsh-part=\"split-row\">\n <header className={css.splitRowHeader}>\n <span className={css.splitRowIndex}>{row.task !== undefined\n ? t(STATUS_KEY[row.task.status]) : t('split.added')}</span>\n <button\n type=\"button\"\n className={css.dangerGhostButton}\n disabled={pending}\n onClick={() => { removeRow(row.key) }}\n >\n {t('split.removeItem')}\n </button>\n </header>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.title')}</span>\n <input\n className={css.input}\n value={row.title}\n disabled={pending}\n onChange={event => { patchRow(row.key, { title: event.target.value }) }}\n />\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.description')}</span>\n <textarea\n className={css.input}\n rows={2}\n value={row.description}\n disabled={pending}\n onChange={event => { patchRow(row.key, { description: event.target.value }) }}\n />\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.prompt')}</span>\n <textarea\n className={css.input}\n rows={2}\n value={row.prompt}\n disabled={pending}\n onChange={event => { patchRow(row.key, { prompt: event.target.value }) }}\n />\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.project')}</span>\n <input\n className={css.input}\n list={projectListId}\n value={row.project}\n disabled={pending}\n onChange={event => { patchRow(row.key, { project: event.target.value }) }}\n />\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.tags')}</span>\n <TagEditor\n tags={row.tags}\n suggestions={distinctTags(all)}\n disabled={pending}\n onChange={tags => { patchRow(row.key, { tags }) }}\n />\n </label>\n {parent !== undefined && (\n <button\n type=\"button\"\n className={css.linkButton}\n disabled={pending}\n onClick={() => { controller.openTask(parent.id) }}\n >\n {t('split.parentOf')}: {parent.title} ⌁\n </button>\n )}\n </div>\n )\n })}\n </div>\n )}\n <datalist id={projectListId}>\n {distinctProjects(all).map(name => (\n <option key={name} value={name} />\n ))}\n </datalist>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('confirm.target')}</span>\n <div className={css.confirmTargetRow} role=\"radiogroup\" aria-label={t('confirm.target')}>\n {BATCH_TARGETS.map(option => (\n <button\n key={option.target}\n type=\"button\"\n role=\"radio\"\n aria-checked={target === option.target}\n className={`${css.confirmTargetButton}${target === option.target ? ` ${css.confirmTargetActive}` : ''}`}\n disabled={pending}\n onClick={() => { setTarget(option.target) }}\n >\n {option.label}\n </button>\n ))}\n </div>\n </label>\n\n {error !== undefined && <p className={css.formError} role=\"alert\">{error}</p>}\n\n <footer className={css.modalFooter}>\n <button type=\"button\" className={css.ghostButton} disabled={pending} onClick={addRow}>\n {t('split.addItem')}\n </button>\n <button\n type=\"button\"\n className={css.dangerGhostButton}\n disabled={pending || rows.length === 0}\n onClick={() => { setConfirmDismissAll(true) }}\n >\n {t('split.dismissAll')}\n </button>\n <button type=\"button\" className={css.ghostButton} onClick={onClose} disabled={pending}>\n {t('new.cancel')}\n </button>\n <button\n type=\"button\"\n className={css.primaryButton}\n disabled={pending || rows.length === 0}\n onClick={() => { void confirmAll() }}\n >\n {pending ? t('board.pending') : t('split.confirmAll', { target: t(target === 'backlog' ? 'confirm.toBacklog' : 'confirm.toTodo') })}\n </button>\n </footer>\n </div>\n\n {confirmDismissAll && (\n <ConfirmDialog\n title={t('split.dismissAllTitle')}\n message={t('split.dismissAllMessage', { count: String(rows.length) })}\n confirmLabel={t('split.dismissAll')}\n danger\n onCancel={() => { setConfirmDismissAll(false) }}\n onConfirm={dismissAll}\n />\n )}\n {removing !== undefined && (() => {\n const row = rows.find(candidate => candidate.key === removing)\n return row?.task === undefined ? null : (\n <ConfirmDialog\n title={t('split.dismissItemTitle')}\n message={t('split.dismissItemMessage', { name: row.title })}\n confirmLabel={t('split.removeItem')}\n danger\n onCancel={() => { setRemoving(undefined) }}\n onConfirm={() => {\n const task = row.task\n setRemoving(undefined)\n if (task !== undefined) controller.dismissTask(task.id)\n setRows(current => current.filter(candidate => candidate.key !== removing))\n }}\n />\n )\n })()}\n </div>\n )\n}\n","/**\n * 任务卡片:列内条目。点击打开任务详情(卡片本身永不直接执行)。\n *\n * **memo 化(§12.2/§17 性能)**:`TaskCard = memo(TaskCardInner)`。看板传给\n * 卡片的 `onClick` 用 useCallback 保持稳定、任务行对象引用在兄弟卡片变化时由\n * 控制器身份保持合并(board-controller.acceptRemote),因此一张卡片只在它\n * 自己的任务变化(或 pending/timeZone 变化)时重渲染,兄弟卡片状态变化、选中\n * 变化、搜索/过滤/归档视图切换不引发全量重渲染。\n *\n * 卡片展示:标题、描述摘要、更新时间、执行次数、运行/提交中 spinner、执行\n * 结果着色、会话标记;定时徽标为数据驱动占位(cron enabled →「定时」,one-shot\n * 未触发 →「计划」,编辑能力随 T006/T007)。\n *\n * T008(§12.8)扩展:项目徽标(高亮)+ 标签 chips——默认最多展示 3 个,溢出\n * 显示 `+N`。徽标/chips 均为任务字段派生的展示,不改变 memo 边界。\n *\n * T009(§12.10)扩展:非归档/非 running/proposed 的卡片 `draggable`,拖动启动\n * 经 `onDragStart`/`onDragEnd` 上报看板(看板持有拖拽状态与落位计算);落点\n * 悬停经 `onDragOverCard`(before/after 判定在卡片内按鼠标 Y 位置计算)。卡片\n * 本身不执行任何动作提交——所有拖拽动作由看板统一经 Host 协议提交。\n */\nimport { memo } from 'react'\nimport type { DragEvent } from 'react'\nimport type { ExecutionRecord, ScheduleRule, TaskRecord, TaskSource } from '../../core/model.ts'\nimport { t, type NovaTaskBoardKey } from '../locales.ts'\nimport css from '../board.module.css'\n\n/** 卡片来源标记的文案键(§9.2/§12.2;暂无专用文案的来源不渲染标记)。 */\nconst SOURCE_KEYS: Partial<Record<TaskSource, NovaTaskBoardKey>> = {\n conversation: 'source.conversation',\n requirement: 'source.requirement',\n}\n\n/** 任务来源 → 文案键;无专用文案返回 undefined(卡片不渲染来源标记)。 */\nexport function sourceKey(source: TaskSource): NovaTaskBoardKey | undefined {\n return SOURCE_KEYS[source]\n}\n\n/** 绝对时间标签(Host 时区)。 */\nexport function formatHostTimestamp(ms: number, timeZone?: string): string {\n try {\n return new Intl.DateTimeFormat(undefined, {\n dateStyle: 'medium',\n timeStyle: 'medium',\n ...(timeZone === undefined ? {} : { timeZone }),\n }).format(new Date(ms))\n } catch {\n return new Date(ms).toISOString()\n }\n}\n\n/** 相对时间(<1m 刚刚;<1h 分钟;<24h 小时;更早回退到 Host 时区日期)。 */\nexport function formatTime(ms: number, timeZone?: string): string {\n const now = Date.now()\n const minutes = Math.floor((now - ms) / 60000)\n if (minutes < 1) return t('time.justNow')\n if (minutes < 60) return `${minutes}m`\n if (minutes < 60 * 24) return `${Math.floor(minutes / 60)}h`\n if (timeZone !== undefined) return formatHostTimestamp(ms, timeZone)\n const date = new Date(ms)\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`\n}\n\n/** 执行结果标签(详情/卡片共用)。 */\nexport function executionLabel(execution: ExecutionRecord): string {\n return execution.result ?? 'running'\n}\n\n/** 定时徽标的数据驱动占位(T006/T007 启用编辑能力,展示自 T004 起)。 */\nexport function scheduleBadge(schedule: ScheduleRule | undefined): { label: string; at?: number } | undefined {\n if (schedule === undefined) return undefined\n if (schedule.kind === 'cron') {\n return schedule.enabled ? { label: t('card.scheduled'), at: schedule.nextRunAt } : undefined\n }\n // one-shot:未消费(未触发/未过期)的计划展示「计划」徽标。\n return schedule.firedAt === undefined ? { label: t('card.oneShot'), at: schedule.runAt } : undefined\n}\n\n/** 卡片标签 chips 的最大展示数(§12.8:默认最多 3 个,溢出显示 +N)。 */\nexport const CARD_TAG_CHIP_LIMIT = 3\n\n/** 项目徽标 + 标签 chips 行(§12.8;任务无项目且无标签时不渲染)。 */\nfunction CardChips({ task }: { task: TaskRecord }) {\n if (task.project === undefined && task.tags.length === 0) return null\n const visible = task.tags.slice(0, CARD_TAG_CHIP_LIMIT)\n const overflow = task.tags.length - visible.length\n return (\n <span className={css.cardChips} data-dsh-part=\"card-chips\">\n {task.project !== undefined && (\n <span className={css.cardProject} title={task.project}>{task.project}</span>\n )}\n {visible.map(tag => (\n <span key={tag} className={css.cardTag} title={tag}>{tag}</span>\n ))}\n {overflow > 0 && <span className={css.cardTagMore}>+{overflow}</span>}\n </span>\n )\n}\n\n/**\n * 列内卡片(memo 化内层)。T009:可拖拽卡片上报 dragstart/dragover/drop/dragend。\n * T011:proposed 候选卡片展示「待确认」角标 + 来源标记(§12.2/§14.4)。\n * T012:确认后的子任务(parentId 存在)展示所属需求(父任务)徽标,可跳转\n * (§12.9/§12.3 父子互跳)。徽标用 span 而非 button——卡片根元素是 button,\n * 避免嵌套交互元素(点击经 stopPropagation 转发 onOpenParent)。\n */\nfunction TaskCardInner({ task, pending, timeZone, onClick, draggable, dragging, onDragStart, onDragEnd, onDragOverCard, onDropCard, parentId, parentTitle, onOpenParent }: {\n task: TaskRecord\n pending: boolean\n timeZone?: string\n onClick: () => void\n /** 是否可拖拽(T009:非归档、非 running/proposed;归档视图不参与拖拽)。 */\n draggable: boolean\n /** 是否正处于拖拽中(视觉高亮来源卡片)。 */\n dragging: boolean\n /** 拖动启动(看板记录拖拽来源;卡片不直接提交任何动作)。 */\n onDragStart: (task: TaskRecord) => void\n /** 拖动结束(看板清理拖拽状态)。 */\n onDragEnd: () => void\n /** 悬停落点(卡片内按鼠标 Y 位置判定 before/after)。 */\n onDragOverCard: (task: TaskRecord, event: DragEvent<HTMLElement>) => void\n /** 松手落点(看板据此提交 move/reorder;卡片不直接提交任何动作)。 */\n onDropCard: (task: TaskRecord, event: DragEvent<HTMLElement>) => void\n /** 父需求任务 id(T012;确认后的子任务展示父徽标)。 */\n parentId?: string\n /** 父需求任务标题(徽标文案;父任务缺失时展示占位文案)。 */\n parentTitle?: string\n /** 父徽标点击:跳转父任务详情(stopPropagation,不触卡片的 open)。 */\n onOpenParent?: (id: string) => void\n}) {\n const latest = task.executions[task.executions.length - 1]\n const runs = task.executions.length\n const archived = task.archivedAt !== undefined\n const badge = scheduleBadge(task.schedule)\n const running = !archived && (task.status === 'running'\n || (latest !== undefined && executionLabel(latest) === 'running'))\n const proposed = !archived && task.status === 'proposed'\n const handleDragStart = (event: DragEvent<HTMLElement>): void => {\n if (!draggable) return\n event.dataTransfer.effectAllowed = 'move'\n // Firefox 需要 setData 才启动拖拽;text/plain 供外部/调试使用。\n event.dataTransfer.setData('text/plain', task.id)\n onDragStart(task)\n }\n return (\n <button\n type=\"button\"\n className={`${css.card}${dragging ? ` ${css.cardDragging}` : ''}`}\n data-status={archived ? 'archived' : task.status}\n data-dsh-part=\"card\"\n data-pending={pending || undefined}\n data-draggable={draggable || undefined}\n draggable={draggable}\n onClick={onClick}\n onDragStart={handleDragStart}\n onDragEnd={onDragEnd}\n onDragOver={event => { onDragOverCard(task, event) }}\n onDrop={event => { onDropCard(task, event) }}\n title={task.description !== '' ? task.description : task.title}\n >\n {proposed && (\n <span className={css.cardProposedBadge} data-dsh-part=\"card-proposed-badge\">\n {t('card.proposedBadge')}\n </span>\n )}\n <span className={css.cardTitle}>{task.title}</span>\n {task.description !== '' && <span className={css.cardExcerpt}>{task.description}</span>}\n <CardChips task={task} />\n {!proposed && parentId !== undefined && (\n <span\n className={css.cardParent}\n data-dsh-part=\"card-parent\"\n role=\"button\"\n tabIndex={0}\n title={parentTitle ?? t('split.parentMissing')}\n onClick={event => { event.stopPropagation(); onOpenParent?.(parentId) }}\n onKeyDown={event => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.stopPropagation()\n onOpenParent?.(parentId)\n }\n }}\n >\n ⤷ {parentTitle ?? t('split.parentMissing')}\n </span>\n )}\n <span className={css.cardMeta}>\n <span className={css.cardTime}>{t('board.updated')} {formatTime(task.updatedAt)}</span>\n {task.source !== undefined && sourceKey(task.source) !== undefined && (\n <span className={css.cardSource} data-source={task.source} title={`${t('card.source')}: ${t(sourceKey(task.source)!)}`}>\n {t(sourceKey(task.source)!)}\n </span>\n )}\n {badge !== undefined && (\n <span\n className={css.cardSchedule}\n title={badge.at !== undefined ? `${badge.label} · ${formatHostTimestamp(badge.at, timeZone)}` : badge.label}\n >\n {badge.at !== undefined ? `${badge.label} · ${formatHostTimestamp(badge.at, timeZone)}` : badge.label}\n </span>\n )}\n {latest !== undefined && (\n <span className={css.cardRun} data-result={archived ? undefined : latest.result}>\n {runs} {t('board.runs')}\n </span>\n )}\n {latest?.sessionId !== undefined && (\n <span className={css.cardSession} title={latest.sessionId}>⌁</span>\n )}\n {!archived && (running || pending) && <span className={css.cardSpinner} aria-hidden=\"true\" />}\n </span>\n {!archived && pending && <span className={css.cardRunningLabel}>{t('board.pending')}…</span>}\n {!archived && running && <span className={css.cardRunningLabel}>{t('detail.result.running')}…</span>}\n </button>\n )\n}\n\n/** Memoized card: re-renders only when the card's own props change. */\nexport const TaskCard = memo(TaskCardInner)\n","/**\n * 5 段 cron 语法校验(DSH-REQ-001 §9.4 / §11.2 协议严格校验「cron 合法性」)。\n *\n * T003 交付语法层(parseCron + isValidCron,纯函数、无依赖、无 fs):协议层\n * 用它对 import 载荷里的 schedule.cron 做合法性校验。T006(cron 周期定时调度)\n * 在本模块之上扩展到期计算(nextRunAtMs,§9.4/§13.1:Host 本地时区、DST 正确\n * 处理、错过不补跑由调度 tick 的滚动语义承担),语法面保持一致。\n *\n * 语法(对齐参考实现 core/schedule.ts 的引擎基线):五个空白分隔字段\n * `分 时 日 月 周`;每字段支持通配星号、步长(星号或区间 + 斜杠 n)、单值、\n * 闭区间 a-b、逗号列表(以上任意混排)。范围:分 0-59、时 0-23、日 1-31、\n * 月 1-12、周 0-7(0 与 7 均表示周日)。非法表达式解析为 null。\n *\n * 与 model.ts `isPlausibleCron` 的分层关系:`isPlausibleCron` 是持久化层的\n * 宽松形状检查(字符集 + 段数,用于行级修复「修复或丢弃 schedule」);\n * 本模块是协议层的严格语法校验(字段取值范围/区间/步长合法性),二者不互换。\n */\n\n/** 解析后的各字段匹配集合(不含周字段 7→0 归一化,见 parseCron)。 */\nexport interface ParsedCron {\n minutes: ReadonlySet<number>\n hours: ReadonlySet<number>\n days: ReadonlySet<number>\n months: ReadonlySet<number>\n weekdays: ReadonlySet<number>\n /** 日字段是否为字面 `*`(未限制)。 */\n dayWildcard: boolean\n /** 周字段是否为字面 `*`(未限制)。 */\n weekdayWildcard: boolean\n}\n\n/** 每字段的闭区间,按 cron 顺序。 */\nconst FIELD_RANGES: ReadonlyArray<readonly [number, number]> = [\n [0, 59], // 分\n [0, 23], // 时\n [1, 31], // 日\n [1, 12], // 月\n [0, 7], // 周(7 = 周日,归一化在 parseCron)\n]\n\n/**\n * 解析一个 5 段 cron 表达式。\n * @returns 各字段匹配集合;表达式非法时返回 null。\n */\nexport function parseCron(expr: string): ParsedCron | null {\n const fields = expr.trim().split(/\\s+/)\n if (fields.length !== 5) return null\n const sets: Set<number>[] = []\n for (let index = 0; index < 5; index += 1) {\n const [min, max] = FIELD_RANGES[index]\n const set = new Set<number>()\n if (!parseField(fields[index], min, max, set)) return null\n sets.push(set)\n }\n const weekdays = new Set<number>()\n for (const day of sets[4]) weekdays.add(day === 7 ? 0 : day)\n return {\n minutes: sets[0],\n hours: sets[1],\n days: sets[2],\n months: sets[3],\n weekdays,\n // 只有字面 `*` 才表示未限制:显式全枚举(如 1-31)是受限字段,\n // 参与日/周 OR 语义(T006 到期计算依赖该区分)。\n dayWildcard: fields[2] === '*',\n weekdayWildcard: fields[4] === '*',\n }\n}\n\n/** 表达式是否可解析(语法合法)。 */\nexport function isValidCron(expr: string): boolean {\n return parseCron(expr) !== null\n}\n\n/**\n * 计算 `fromMs`(ms epoch)之后的下一个匹配时刻(T006 §9.4/§13.1)。返回匹配\n * 分钟起点的 ms epoch,**严格大于** fromMs;日历约束永不可能满足时返回\n * undefined(如 `0 0 30 2 *`——五年视界包含完整闰周期,合法的 2 月 29 日\n * 计划在任意非闰年仍可达)。\n *\n * 语义(对齐参考实现 core/schedule.ts,T006 验收 3):\n * - **Host 本地时区**:按 `new Date(y, m, d, h, min)` 墙钟字段遍历候选,不换算\n * 到 UTC——DST 由本地时间语义自然承担;\n * - **日/周 OR 语义**:日字段与周字段字面 `*` 时各自视为未限制(见 parseCron),\n * 二者都受限时用 OR 组合(标准 cron);\n * - **DST 正确性**:墙钟字段构造 + 最终 `matches` 复检复现旧分钟扫描的 DST\n * 语义——春令时不存在的时间(如 02:30 → 03:30)被 JS 归一化前移,回拨的\n * 重复小时(fall-back)只访问第一次出现;本实现不逐分钟扫描,而是直接从\n * 解析后的字段集合遍历年/月/日/时/分候选,稀疏表达式(如 `0 0 29 2 *`)不会\n * 扫过约 150 万分钟。\n */\nexport function nextRunAtMs(expr: string, fromMs: number): number | undefined {\n const schedule = parseCron(expr)\n if (schedule === null) return undefined\n if (!hasPossibleCalendarDay(schedule)) return undefined\n const from = new Date(fromMs)\n const limitMs = fromMs + 5 * 366 * 24 * 60 * 60 * 1000\n\n const sortedMinutes = [...schedule.minutes].sort((a, b) => a - b)\n const sortedHours = [...schedule.hours].sort((a, b) => a - b)\n const sortedMonths = [...schedule.months].sort((a, b) => a - b)\n\n let year = from.getFullYear()\n let month = from.getMonth() + 1\n let day = from.getDate()\n let hour = from.getHours()\n // 严格大于 fromMs:旧分钟扫描从下一分钟开始。\n let minute = from.getMinutes() + 1\n\n while (new Date(year, month - 1, 1, 0, 0, 0, 0).getTime() <= limitMs) {\n for (const candidateMonth of sortedMonths) {\n if (candidateMonth < month) continue\n const daysInMonth = new Date(year, candidateMonth, 0).getDate()\n const dayStart = candidateMonth === month ? day : 1\n for (let candidateDay = dayStart; candidateDay <= daysInMonth; candidateDay += 1) {\n const dayProbe = new Date(year, candidateMonth - 1, candidateDay, 0, 0, 0, 0)\n if (!dayCandidate(schedule, dayProbe)) continue\n const hourStart = candidateMonth === month && candidateDay === day ? hour : 0\n for (const candidateHour of sortedHours) {\n if (candidateHour < hourStart) continue\n const minuteStart = candidateMonth === month && candidateDay === day && candidateHour === hour ? minute : 0\n for (const candidateMinute of sortedMinutes) {\n if (candidateMinute < minuteStart) continue\n const candidate = new Date(year, candidateMonth - 1, candidateDay, candidateHour, candidateMinute, 0, 0)\n const time = candidate.getTime()\n if (time <= fromMs) continue\n if (time > limitMs) return undefined\n if (matches(schedule, candidate)) return time\n }\n }\n }\n }\n year += 1\n month = 1\n day = 1\n hour = 0\n minute = 0\n }\n return undefined\n}\n\n/** 日/周 OR 门(`matches` 与候选扫描共用)。 */\nfunction dayCandidate(schedule: ParsedCron, date: Date): boolean {\n const dayMatches = schedule.days.has(date.getDate())\n const weekdayMatches = schedule.weekdays.has(date.getDay())\n if (schedule.dayWildcard) return weekdayMatches\n if (schedule.weekdayWildcard) return dayMatches\n return dayMatches || weekdayMatches\n}\n\n/** 拒绝不可能匹配的月/日组合,免去多年扫描(如 `0 0 30 2 *`)。 */\nfunction hasPossibleCalendarDay(schedule: ParsedCron): boolean {\n if (schedule.dayWildcard || !schedule.weekdayWildcard) return true\n const maximumDay = new Map<number, number>([\n [1, 31], [2, 29], [3, 31], [4, 30], [5, 31], [6, 30],\n [7, 31], [8, 31], [9, 30], [10, 31], [11, 30], [12, 31],\n ])\n for (const month of schedule.months) {\n const maximum = maximumDay.get(month) ?? 0\n if ([...schedule.days].some(day => day <= maximum)) return true\n }\n return false\n}\n\n/** 日/周 OR 语义:受限的日字段单独门控,周字段反之亦然(标准 cron)。 */\nfunction matches(schedule: ParsedCron, date: Date): boolean {\n if (!schedule.minutes.has(date.getMinutes())) return false\n if (!schedule.hours.has(date.getHours())) return false\n if (!schedule.months.has(date.getMonth() + 1)) return false\n return dayCandidate(schedule, date)\n}\n\n/** 解析一个逗号列表字段,写入匹配集合;非法返回 false。 */\nfunction parseField(field: string, min: number, max: number, out: Set<number>): boolean {\n if (field === '*') {\n for (let value = min; value <= max; value += 1) out.add(value)\n return true\n }\n for (const part of field.split(',')) {\n if (part === '') return false\n const [range, stepRaw] = part.split('/')\n let low: number\n let high: number\n if (range === '*') {\n low = min\n high = max\n } else if (range.includes('-')) {\n const [a, b] = range.split('-')\n if (a === '' || b === '' || !isDigits(a) || !isDigits(b)) return false\n low = Number(a)\n high = Number(b)\n } else if (isDigits(range)) {\n low = Number(range)\n high = Number(range)\n } else {\n return false\n }\n if (low < min || high > max || low > high) return false\n const step = stepRaw === undefined ? 1 : isDigits(stepRaw) ? Number(stepRaw) : NaN\n if (!Number.isInteger(step) || step < 1) return false\n for (let value = low; value <= high; value += step) out.add(value)\n }\n return true\n}\n\nfunction isDigits(value: string): boolean {\n return /^\\d+$/.test(value)\n}\n","/**\n * 定时编辑器共享预设(T006 §12.3 cron 周期定时「预设」下拉 + T007 §12.3 一次性\n * 计划「快捷预设」):cron 表达式 → locale 文案键;one-shot 快捷预设 → 相对\n * 当前时刻的计算函数(Host 本地时区)。详情页定时编辑器共用。\n */\nimport type { NovaTaskBoardKey } from './locales.ts'\n\n/** 常用 cron 定时预设(cron → locale label)。 */\nexport const SCHEDULE_PRESETS: ReadonlyArray<{ cron: string; label: NovaTaskBoardKey }> = [\n { cron: '0 9 * * *', label: 'detail.schedule.preset.daily9' },\n { cron: '0 * * * *', label: 'detail.schedule.preset.hourly' },\n { cron: '*/10 * * * *', label: 'detail.schedule.preset.tenMin' },\n { cron: '0 9 * * 1', label: 'detail.schedule.preset.weeklyMon9' },\n]\n\n/** 一次性计划快捷预设(T007 §12.3:label → 相对 now 的 runAt 计算)。 */\nexport const ONE_SHOT_PRESETS: ReadonlyArray<{ label: NovaTaskBoardKey; at: (now: number) => number }> = [\n { label: 'detail.oneShot.preset.tenMin', at: now => now + 10 * 60_000 },\n {\n label: 'detail.oneShot.preset.tonight21',\n at: (now) => {\n const date = new Date(now)\n date.setHours(21, 0, 0, 0)\n return date.getTime() > now ? date.getTime() : date.getTime() + 24 * 60 * 60 * 1000\n },\n },\n {\n label: 'detail.oneShot.preset.tomorrow9',\n at: (now) => {\n const date = new Date(now)\n date.setDate(date.getDate() + 1)\n date.setHours(9, 0, 0, 0)\n return date.getTime()\n },\n },\n]\n\n/** 本地毫秒时间戳 → `datetime-local` 输入值(YYYY-MM-DDTHH:mm,Host 本地时区)。 */\nexport function toLocalInputValue(ms: number): string {\n const date = new Date(ms)\n const pad = (value: number): string => String(value).padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/** `datetime-local` 输入值 → 本地毫秒时间戳(解析失败返回 undefined)。 */\nexport function fromLocalInputValue(value: string): number | undefined {\n const ms = new Date(value).getTime()\n return Number.isFinite(ms) ? ms : undefined\n}\n","/**\n * 任务详情(T004 §12.3):描述、执行 Prompt、项目与标签编辑(T008 §12.8)、\n * 执行设置、执行历史、状态移动、执行/重执行/归档/恢复/删除(二次确认)。\n *\n * T012(§12.3/§12.9):父需求链接(子任务顶部展示所属需求并可跳转)、子任务\n * 列表(父任务详情展示可逐个跳转)、「从需求拆分」递归拆分入口(prefill 标题\n * 并携带 parentTaskId——新父需求任务挂到该任务下)。\n *\n * - 执行 Prompt 为空时以标题呈现;归档任务隐藏执行设置(只读,§19-6);\n * - running/pending 时相关按钮禁用;执行按钮经 `executionCapable`(T005 起恒\n * true,协议已并入 run/rerun)门控;\n * - 执行历史:结果徽标 + 起止时间 + 一键跳转会话 transcript + 错误信息\n * (§19-5;会话跳转经 sessions.open,会话由 HostRunner 创建,T005);\n * - 状态移动仅提供合法目标(待规划/待办;done/failed 移回视为重开,不触发执行);\n * - 定时编辑(T006 §12.3 cron + T007 §12.3 one-shot,二选一):编辑器顶部是\n * 「周期定时 / 一次性计划」模式切换(§9.4 判别联合);cron 为「开关 + cron\n * 输入 + 预设 + 下次/上次运行时间」;one-shot 为「日期时间选择器(datetime-local,\n * Host 本地时区)+ 快捷预设 + 计划时刻/剩余时间 + 取消计划」,已触发显示\n * 「已执行 · {时间}」,已错过显示「已过期/已跳过」并提示重新设定。\n */\nimport { useEffect, useId, useState } from 'react'\nimport type { ExecutionRecord, TaskPermission, TaskRecord, TaskStatus } from '../../core/model.ts'\nimport { TASK_PERMISSIONS } from '../../core/model.ts'\nimport { isValidCron } from '../../core/cron.ts'\nimport type { BoardController } from '../board-controller.ts'\nimport { distinctProjects, distinctTags } from '../filter.ts'\nimport { t, type NovaTaskBoardKey } from '../locales.ts'\nimport { fromLocalInputValue, ONE_SHOT_PRESETS, SCHEDULE_PRESETS, toLocalInputValue } from '../schedule-presets.ts'\nimport css from '../board.module.css'\nimport { ConfirmDialog } from './ConfirmDialog.tsx'\nimport { RequirementSplitModal } from './RequirementSplitModal.tsx'\nimport { TagEditor } from './TagEditor.tsx'\nimport { executionLabel, formatHostTimestamp, formatTime } from './TaskCard.tsx'\nimport { STATUS_KEY } from './status-key.ts'\n\n/** 手动移动的合法目标(§10.2:backlog/todo 互移、done/failed 重开落回)。 */\nconst MOVE_TARGETS: readonly TaskStatus[] = ['backlog', 'todo']\n\n/** 执行结果 → 文案键。 */\nconst RESULT_KEY: Record<NonNullable<ExecutionRecord['result']>, NovaTaskBoardKey> = {\n succeeded: 'detail.result.succeeded',\n failed: 'detail.result.failed',\n cancelled: 'detail.result.cancelled',\n}\n\n/** 执行历史单行:结果徽标 + 起止时间 + 跳转会话 + 错误信息。 */\nfunction ExecutionRow({ execution, timeZone, onOpen }: {\n execution: ExecutionRecord\n timeZone?: string\n onOpen: (sessionId: string) => void\n}) {\n const result = execution.result\n return (\n <li className={css.executionRow} data-result={result}>\n <span className={css.executionBadge} data-result={result}>\n {result === undefined ? t('detail.result.running') : t(RESULT_KEY[result])}\n </span>\n <span className={css.executionTimes}>\n {t('detail.executionStarted')} {formatTime(execution.startedAt, timeZone)}\n {execution.endedAt !== undefined && ` · ${t('detail.executionEnded')} ${formatTime(execution.endedAt, timeZone)}`}\n </span>\n {execution.sessionId !== undefined && (\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { onOpen(execution.sessionId as string) }}\n title={execution.sessionId}\n >\n {t('detail.viewSession')} ⌁\n </button>\n )}\n {execution.error !== undefined && execution.error !== '' && (\n <span className={css.executionError}>{execution.error}</span>\n )}\n </li>\n )\n}\n\n/** 执行设置(工作区/模式/权限下拉,§12.3):钉住目标被移除时以「已移除」行保留展示。 */\nfunction ExecutionSettingsSection({ controller, task, pending }: {\n controller: BoardController\n task: TaskRecord\n pending: boolean\n}) {\n const [options, setOptions] = useState(controller.getSnapshot().executionOptions)\n useEffect(\n () => controller.subscribe(() => setOptions(controller.getSnapshot().executionOptions)),\n [controller],\n )\n const workspaceId = task.workspaceId ?? ''\n const mode = task.mode ?? ''\n const permission = task.permission ?? ''\n const workspaceKnown = workspaceId === '' || options.workspaces.some(item => item.workspaceId === workspaceId)\n const modeKnown = mode === '' || options.presets.some(item => item.id === mode)\n return (\n <section className={css.detailSection}>\n <h4>{t('detail.executionSettings')}</h4>\n <p className={css.detailText}>{t('exec.hint')}</p>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.workspace')}</span>\n <select\n className={css.select}\n value={workspaceId}\n disabled={pending}\n onChange={event => { controller.updateTask(task.id, { workspaceId: event.target.value }) }}\n >\n <option value=\"\">{t('exec.workspace.recent')}</option>\n {!workspaceKnown && <option value={workspaceId}>{workspaceId}{t('exec.mode.removed')}</option>}\n {options.workspaces.map(workspace => (\n <option key={workspace.workspaceId} value={workspace.workspaceId}>{workspace.title}</option>\n ))}\n </select>\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.mode')}</span>\n <select\n className={css.select}\n value={mode}\n disabled={pending}\n onChange={event => { controller.updateTask(task.id, { mode: event.target.value }) }}\n >\n <option value=\"\">{t('exec.mode.default')}</option>\n {!modeKnown && <option value={mode}>{mode}{t('exec.mode.removed')}</option>}\n {options.presets.map(preset => (\n <option key={preset.id} value={preset.id} disabled={preset.broken !== undefined}>\n {preset.name ?? preset.id}\n {preset.isDefault ? t('exec.mode.defaultSuffix') : ''}\n {preset.broken !== undefined ? t('exec.mode.brokenSuffix') : ''}\n </option>\n ))}\n </select>\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.permission')}</span>\n <select\n className={css.select}\n value={permission}\n disabled={pending}\n onChange={event => { controller.updateTask(task.id, { permission: event.target.value === '' ? undefined : event.target.value as TaskPermission }) }}\n >\n <option value=\"\">{t('exec.permission.default')}</option>\n {TASK_PERMISSIONS.map(id => (\n <option key={id} value={id}>{t(`exec.permission.${id}` as NovaTaskBoardKey)}</option>\n ))}\n </select>\n </label>\n </section>\n )\n}\n\n/**\n * 项目与标签编辑(T008 §12.8):项目输入(datalist 建议账本已有项目,失焦/\n * Enter 提交,空串清除)+ 标签 chips 编辑(TagEditor,整体替换落账)。\n *\n * - 本地草稿跟随账本(useEffect 以 task 字段为依赖);提交经 update patch\n * 走 Host,账本确认后回填(与执行设置下拉同一模式);\n * - 项目/标签建议由当前账本全部任务推导(distinctProjects/distinctTags),\n * 只读快照、不订阅——TaskDetail 随控制器快照重渲染即可刷新。\n */\nfunction ProjectTagsSection({ controller, task, pending }: {\n controller: BoardController\n task: TaskRecord\n pending: boolean\n}) {\n const [project, setProject] = useState(task.project ?? '')\n const [tags, setTags] = useState(task.tags)\n const listId = useId()\n useEffect(() => {\n setProject(task.project ?? '')\n setTags(task.tags)\n }, [task.id, task.project, task.tags])\n const all = controller.getSnapshot().tasks\n const projectSuggestions = distinctProjects(all)\n const tagSuggestions = distinctTags(all)\n const commitProject = (): void => {\n const value = project.trim()\n if (value === (task.project ?? '')) return\n controller.updateTask(task.id, { project: value === '' ? undefined : value })\n }\n return (\n <section className={css.detailSection}>\n <h4>{t('detail.projectTags')}</h4>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.project')}</span>\n <input\n className={css.input}\n list={listId}\n value={project}\n disabled={pending}\n placeholder={t('detail.projectPlaceholder')}\n onChange={event => { setProject(event.target.value) }}\n onBlur={commitProject}\n onKeyDown={event => { if (event.key === 'Enter') commitProject() }}\n />\n <datalist id={listId}>\n {projectSuggestions.filter(name => name !== task.project).map(name => (\n <option key={name} value={name} />\n ))}\n </datalist>\n </label>\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('filter.tags')}</span>\n <TagEditor\n tags={tags}\n suggestions={tagSuggestions}\n disabled={pending}\n onChange={next => { controller.updateTask(task.id, { tags: next }) }}\n />\n </label>\n </section>\n )\n}\n\n/** 执行安排编辑模式(§9.4 判别联合二选一)。 */\ntype ScheduleMode = 'cron' | 'one-shot'/** 一次性计划状态(§13.5:firedAt 区分「已执行 / 已跳过」)。 */\ntype OneShotStatus =\n | { kind: 'pending' } // 未触发(计划中)\n | { kind: 'executed'; at: number } // 已执行\n | { kind: 'skipped'; at: number } // 已过期/已跳过\n\n/**\n * 一次性计划状态判定:firedAt 缺省 → pending;firedAt 已写 → 依「是否真的有\n * 一次执行落账」区分——触发时任务 running 冲突/错过恢复只写 firedAt 不开执行\n * (skipped),正常触发会追加 startedAt ≈ firedAt 的执行记录(executed)。\n */\nfunction oneShotStatus(task: TaskRecord): OneShotStatus {\n const schedule = task.schedule\n if (schedule === undefined || schedule.kind !== 'one-shot') return { kind: 'pending' }\n const firedAt = schedule.firedAt\n if (firedAt === undefined) return { kind: 'pending' }\n const fired = task.executions.some(execution => execution.startedAt >= firedAt - 1_000)\n return fired ? { kind: 'executed', at: firedAt } : { kind: 'skipped', at: firedAt }\n}\n\n/** 剩余时间文案(runAt 距今;已到点 → undefined 由调用方显示「即将触发」)。 */\nfunction remainingLabel(runAt: number, now: number): string | undefined {\n const diff = runAt - now\n if (diff <= 0) return undefined\n const minutes = Math.floor(diff / 60000)\n if (minutes < 60) return `${t('detail.oneShot.remaining')} ${minutes}m`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${t('detail.oneShot.remaining')} ${hours}h ${minutes % 60}m`\n return `${t('detail.oneShot.remaining')} ${Math.floor(hours / 24)}d`\n}\n\n/**\n * cron 周期定时编辑器(T006 §12.3):开关 + cron 输入 + 预设 + 下次/上次运行\n * 时间。变更经 set-schedule action(enabled/cron patch)提交 Host,nextRunAt/\n * lastTriggeredAt 由 Host 调度器维护(§9.4 浏览器不可写),这里只读展示。\n *\n * - 开关:武装/解除(解除保留 cron 配置;武装时若输入与账本不一致则一并提交);\n * - cron 输入:Enter/失焦提交(客户端先做合法性预检,非法仅本地报错不提交,\n * Host 端状态机再校验);预设下拉一键填入并提交;\n * - 下次运行 <= 现在(已到期待 tick)显示「即将运行」,无计划显示「未安排」。\n */\nfunction CronEditor({ controller, task, pending }: {\n controller: BoardController\n task: TaskRecord\n pending: boolean\n}) {\n const schedule = task.schedule?.kind === 'cron' ? task.schedule : undefined\n const [cron, setCron] = useState(schedule?.cron ?? '0 9 * * *')\n const [enabled, setEnabled] = useState(schedule?.enabled ?? false)\n const [nextRunAt, setNextRunAt] = useState<number | undefined>(schedule?.nextRunAt)\n const [lastTriggeredAt, setLastTriggeredAt] = useState<number | undefined>(schedule?.lastTriggeredAt)\n const [error, setError] = useState<string | undefined>(undefined)\n const timeZone = controller.getSnapshot().host?.scheduler.timeZone\n\n // 任务记录在账本更新后变化(开关确认、到期滚动):跟随最新 schedule 渲染。\n useEffect(() => {\n setCron(schedule?.cron ?? '0 9 * * *')\n setEnabled(schedule?.enabled ?? false)\n setNextRunAt(schedule?.nextRunAt)\n setLastTriggeredAt(schedule?.lastTriggeredAt)\n setError(undefined)\n }, [task.id, schedule?.cron, schedule?.enabled, schedule?.nextRunAt, schedule?.lastTriggeredAt])\n\n /** 校验并持久化当前 cron 文本(Enter/失焦)。非法仅本地报错,不提交。 */\n const saveCron = (value: string): void => {\n const trimmed = value.trim()\n setCron(trimmed)\n if (trimmed === '' || !isValidCron(trimmed)) {\n setError(t('detail.schedule.invalid'))\n return\n }\n setError(undefined)\n controller.setSchedule(task.id, { cron: trimmed })\n }\n\n /** 武装/解除(§11.2 enabled patch);武装前先持久化已编辑的 cron。 */\n const toggleEnabled = (next: boolean): void => {\n const trimmed = cron.trim()\n if (next && (trimmed === '' || !isValidCron(trimmed))) {\n setError(t('detail.schedule.invalid'))\n return\n }\n setError(undefined)\n controller.setSchedule(task.id, {\n enabled: next,\n ...(next && trimmed !== schedule?.cron ? { cron: trimmed } : {}),\n })\n }\n\n /** 预设一键填入并提交(保持当前 enabled 状态;Host 重算 nextRunAt)。 */\n const applyPreset = (preset: string): void => {\n if (preset === '') return\n setCron(preset)\n setError(undefined)\n controller.setSchedule(task.id, { cron: preset })\n }\n\n const nextLabel = !enabled || nextRunAt === undefined\n ? t('detail.schedule.notScheduled')\n : nextRunAt <= Date.now()\n ? t('detail.schedule.dueSoon')\n : formatHostTimestamp(nextRunAt, timeZone)\n const lastLabel = lastTriggeredAt === undefined ? '—' : formatHostTimestamp(lastTriggeredAt, timeZone)\n\n return (\n <>\n <label className={css.scheduleToggle}>\n <input\n type=\"checkbox\"\n checked={enabled}\n disabled={pending}\n onChange={event => { toggleEnabled(event.target.checked) }}\n />\n <span>{t('detail.schedule.enable')}</span>\n </label>\n <div className={css.scheduleRow}>\n <input\n className={`${css.input} ${css.scheduleInput}${error !== undefined ? ` ${css.scheduleInputInvalid}` : ''}`}\n value={cron}\n disabled={pending}\n placeholder=\"0 9 * * *\"\n spellCheck={false}\n aria-label={t('detail.schedule.cron')}\n onChange={event => { setCron(event.target.value); setError(undefined) }}\n onBlur={() => { saveCron(cron) }}\n onKeyDown={event => { if (event.key === 'Enter') saveCron(cron) }}\n />\n <select\n className={css.schedulePreset}\n value=\"\"\n disabled={pending}\n aria-label={t('detail.schedule.presets')}\n onChange={event => { applyPreset(event.target.value) }}\n >\n <option value=\"\">{t('detail.schedule.presets')}…</option>\n {SCHEDULE_PRESETS.map(preset => (\n <option key={preset.cron} value={preset.cron}>{t(preset.label)}</option>\n ))}\n </select>\n </div>\n {error !== undefined && <p className={css.formError}>{error}</p>}\n <p className={css.scheduleMeta}>\n {t('detail.schedule.nextRun')} {nextLabel}\n {' · '}{t('detail.schedule.lastTriggered')} {lastLabel}\n </p>\n </>\n )\n}\n\n/**\n * one-shot 一次性计划编辑器(T007 §12.3):日期时间选择器(datetime-local,\n * Host 本地时区)+ 快捷预设 + 计划时刻/剩余时间 + 取消计划。\n *\n * - 变更经 set-one-shot action(runAt patch)提交 Host,firedAt 为 Host 独占\n * 字段(§9.4 浏览器不可写),这里只读展示;\n * - 未触发 → 计划时刻 + 剩余时间(已到期待 tick → 「即将触发」);已触发 →\n * 「已执行 · {时间}」;已错过 → 「已过期/已跳过」+ 提示重新设定;\n * - 「取消计划」清空 runAt(§9.4 未到时刻前可取消)。\n */\nfunction OneShotEditor({ controller, task, pending }: {\n controller: BoardController\n task: TaskRecord\n pending: boolean\n}) {\n const schedule = task.schedule?.kind === 'one-shot' ? task.schedule : undefined\n const [value, setValue] = useState(toLocalInputValue(schedule?.runAt ?? Date.now()))\n const [error, setError] = useState<string | undefined>(undefined)\n const timeZone = controller.getSnapshot().host?.scheduler.timeZone\n const status = oneShotStatus(task)\n const now = Date.now()\n\n // 任务记录在账本更新后变化(设定确认、触发消费):跟随最新 schedule 渲染。\n useEffect(() => {\n setValue(toLocalInputValue(schedule?.runAt ?? Date.now()))\n setError(undefined)\n }, [task.id, schedule?.runAt])\n\n /** 提交当前 datetime-local 值(runAt ms)。解析失败/过期输入仅本地报错。 */\n const submit = (): void => {\n const runAt = fromLocalInputValue(value)\n if (runAt === undefined) {\n setError(t('detail.oneShot.invalid'))\n return\n }\n setError(undefined)\n controller.setOneShot(task.id, { runAt })\n }\n\n /** 快捷预设:填入并立即提交(label → 相对 now 的计算,§12.3)。 */\n const applyPreset = (at: number): void => {\n setValue(toLocalInputValue(at))\n setError(undefined)\n controller.setOneShot(task.id, { runAt: at })\n }\n\n const runAtLabel = schedule === undefined\n ? t('detail.schedule.notScheduled')\n : schedule.runAt <= now\n ? t('detail.oneShot.dueSoon')\n : `${formatHostTimestamp(schedule.runAt, timeZone)}${remainingLabel(schedule.runAt, now) !== undefined ? ` · ${remainingLabel(schedule.runAt, now)}` : ''}`\n\n return (\n <>\n <div className={css.scheduleRow}>\n <input\n type=\"datetime-local\"\n className={`${css.input} ${css.scheduleInput}${error !== undefined ? ` ${css.scheduleInputInvalid}` : ''}`}\n value={value}\n disabled={pending}\n aria-label={t('detail.oneShot.runAt')}\n onChange={event => { setValue(event.target.value); setError(undefined) }}\n onBlur={submit}\n onKeyDown={event => { if (event.key === 'Enter') submit() }}\n />\n </div>\n <div className={css.scheduleRow}>\n {ONE_SHOT_PRESETS.map(preset => (\n <button\n key={preset.label}\n type=\"button\"\n className={css.ghostButton}\n disabled={pending}\n onClick={() => { applyPreset(preset.at(now)) }}\n >\n {t(preset.label)}\n </button>\n ))}\n <button\n type=\"button\"\n className={css.dangerGhostButton}\n disabled={pending || schedule === undefined}\n onClick={() => { controller.setOneShot(task.id, {}) }}\n >\n {t('detail.oneShot.cancel')}\n </button>\n </div>\n {error !== undefined && <p className={css.formError}>{error}</p>}\n <p className={css.scheduleMeta}>\n {t('detail.oneShot.runAt')} {runAtLabel}\n {status.kind === 'executed' && ` · ${t('detail.oneShot.executed', { time: formatHostTimestamp(status.at, timeZone) })}`}\n {status.kind === 'skipped' && ` · ${t('detail.oneShot.skipped')}(${t('detail.oneShot.reschedule')})`}\n </p>\n </>\n )\n}\n\n/**\n * 执行安排编辑器(T006 cron + T007 one-shot,§12.3 二选一):顶部模式切换\n * (周期定时 / 一次性计划,§9.4 判别联合)。切换仅是本地 UI 态、不落账——\n * 只有提交具体计划(武装 cron / 设定 runAt / 取消)才经 set-schedule /\n * set-one-shot 生效,Host 端保证互斥(D6);任务在账本更新后跟随其 schedule\n * 分支同步当前模式。\n */\nfunction ScheduleSection({ controller, task, pending }: {\n controller: BoardController\n task: TaskRecord\n pending: boolean\n}) {\n const kind = task.schedule?.kind ?? 'cron'\n const [mode, setMode] = useState<ScheduleMode>(kind)\n useEffect(() => { setMode(kind) }, [task.id, kind])\n return (\n <section className={css.detailSection}>\n <h4>{t('detail.schedule')}</h4>\n <div className={css.scheduleMode} role=\"tablist\" aria-label={t('detail.schedule')}>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === 'cron'}\n className={`${css.scheduleModeButton}${mode === 'cron' ? ` ${css.scheduleModeActive}` : ''}`}\n onClick={() => { setMode('cron') }}\n >\n {t('detail.schedule.modeCron')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === 'one-shot'}\n className={`${css.scheduleModeButton}${mode === 'one-shot' ? ` ${css.scheduleModeActive}` : ''}`}\n onClick={() => { setMode('one-shot') }}\n >\n {t('detail.schedule.modeOneShot')}\n </button>\n </div>\n {mode === 'cron'\n ? <CronEditor controller={controller} task={task} pending={pending} />\n : <OneShotEditor controller={controller} task={task} pending={pending} />}\n </section>\n )\n}\n\n/** 任务详情浮层。 */\nexport function TaskDetail({ controller, task }: { controller: BoardController; task: TaskRecord }) {\n const [confirmDelete, setConfirmDelete] = useState(false)\n const [showSplit, setShowSplit] = useState(false)\n\n // 任务记录在账本更新后变化(如 Host 确认移动):跟随最新记录渲染。\n const [latest, setLatest] = useState(task)\n useEffect(() => { setLatest(task) }, [task])\n const current = latest\n const snapshot = controller.getSnapshot()\n const running = current.status === 'running'\n const archived = current.archivedAt !== undefined\n const pending = snapshot.pendingTaskIds.includes(current.id)\n const transportError = snapshot.transportError\n const timeZone = snapshot.host?.scheduler.timeZone\n const canRun = snapshot.executionCapable && !running && !pending\n const parent = current.parentId === undefined ? undefined\n : snapshot.tasks.find(candidate => candidate.id === current.parentId)\n const children = snapshot.tasks.filter(candidate => candidate.parentId === current.id && candidate.archivedAt === undefined)\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) controller.closeTask() }}>\n <div className={css.detail} role=\"dialog\" aria-label={t('detail.title')}>\n <header className={css.detailHeader}>\n <h2 className={css.detailTitle}>{current.title}</h2>\n <span className={css.statusBadge} data-status={archived ? 'archived' : current.status}>\n {archived ? t('board.archive') : t(STATUS_KEY[current.status])}\n </span>\n <button\n type=\"button\"\n className={css.iconButton}\n aria-label={t('detail.close')}\n onClick={() => { controller.closeTask() }}\n >\n ×\n </button>\n </header>\n\n <div className={css.detailBody}>\n {transportError !== undefined && (\n <div className={css.formError} role=\"alert\">\n {t('board.hostError', { error: transportError })}{' '}\n <button type=\"button\" className={css.linkButton} onClick={() => { void controller.retryHostSync() }}>\n {t('board.retryHost')}\n </button>\n </div>\n )}\n {current.parentId !== undefined && (\n <section className={css.detailSection} data-dsh-part=\"parent-link\">\n <h4>{t('split.parentOf')}</h4>\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { controller.openTask(current.parentId!) }}\n >\n {parent?.title ?? t('split.parentMissing')} ⌁\n </button>\n </section>\n )}\n\n <section className={css.detailSection}>\n <h4>{t('detail.description')}</h4>\n <p className={css.detailText}>{current.description !== '' ? current.description : '—'}</p>\n </section>\n\n <section className={css.detailSection}>\n <h4>{t('detail.prompt')}</h4>\n <pre className={css.promptBlock}>{current.prompt !== '' ? current.prompt : current.title}</pre>\n </section>\n\n {!archived && (\n <>\n <ProjectTagsSection controller={controller} task={current} pending={pending} />\n <ExecutionSettingsSection controller={controller} task={current} pending={pending} />\n <ScheduleSection controller={controller} task={current} pending={pending} />\n </>\n )}\n\n <section className={css.detailSection}>\n <h4>{t('detail.execution')}</h4>\n {current.executions.length === 0 ? (\n <p className={css.detailText}>{t('detail.noExecution')}</p>\n ) : (\n <ul className={css.executionList}>\n {[...current.executions].reverse().map(execution => (\n <ExecutionRow\n key={execution.id}\n execution={execution}\n timeZone={timeZone}\n onOpen={sessionId => { controller.openSession(sessionId) }}\n />\n ))}\n </ul>\n )}\n </section>\n\n {children.length > 0 && (\n <section className={css.detailSection} data-dsh-part=\"children-list\">\n <h4>{t('split.children')}({children.length})</h4>\n <ul className={css.executionList}>\n {children.map(child => (\n <li key={child.id} className={css.executionRow}>\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { controller.openTask(child.id) }}\n >\n {child.title}\n </button>\n <span className={css.executionBadge} data-status={child.status}>\n {t(STATUS_KEY[child.status])}\n </span>\n </li>\n ))}\n </ul>\n </section>\n )}\n\n {!archived && (\n <section className={css.detailSection}>\n <h4>{t('board.status')}</h4>\n <div className={css.moveRow}>\n {MOVE_TARGETS.map(status => (\n <button\n key={status}\n type=\"button\"\n className={css.ghostButton}\n disabled={current.status === status || running || pending}\n onClick={() => { controller.moveTask(current.id, status) }}\n >\n {t(`status.move.${status}` as NovaTaskBoardKey)}\n </button>\n ))}\n </div>\n </section>\n )}\n </div>\n\n <footer className={css.detailFooter}>\n {!archived && pending && <span className={css.detailMeta}>{t('board.pending')}…</span>}\n {!archived && (\n <button\n type=\"button\"\n className={css.ghostButton}\n disabled={pending}\n onClick={() => { setShowSplit(true) }}\n >\n {t('split.entry')}\n </button>\n )}\n {!archived && (\n <button\n type=\"button\"\n className={css.primaryButton}\n disabled={!canRun}\n onClick={() => {\n void controller.rerunTask(current.id).then(() => {\n if (controller.getSnapshot().transportError === undefined) controller.closeTask()\n })\n }}\n >\n {current.executions.length === 0 ? t('detail.run') : t('detail.rerun')}\n </button>\n )}\n {archived ? (\n <button\n type=\"button\"\n className={css.primaryButton}\n disabled={pending}\n onClick={() => { controller.restoreTask(current.id) }}\n >\n {t('detail.restore')}\n </button>\n ) : (\n (current.status === 'done' || current.status === 'failed') && (\n <button\n type=\"button\"\n className={css.ghostButton}\n disabled={pending}\n onClick={() => { controller.archiveTask(current.id) }}\n >\n {t('detail.archive')}\n </button>\n )\n )}\n <button\n type=\"button\"\n className={css.dangerButton}\n disabled={pending}\n onClick={() => { setConfirmDelete(true) }}\n >\n {t('detail.delete')}\n </button>\n <span className={css.detailMeta}>\n {t('board.created')} {formatTime(current.createdAt, timeZone)}\n {archived && ` · ${t('detail.archivedAt', { time: formatTime(current.archivedAt!, timeZone) })}`}\n </span>\n </footer>\n </div>\n\n {confirmDelete && (\n <ConfirmDialog\n title={t('delete.title')}\n message={t('delete.confirm', { name: current.title })}\n confirmLabel={t('delete.ok')}\n danger\n onCancel={() => { setConfirmDelete(false) }}\n onConfirm={() => {\n setConfirmDelete(false)\n controller.deleteTask(current.id)\n }}\n />\n )}\n {showSplit && (\n <RequirementSplitModal\n controller={controller}\n prefill={{ title: current.title, parentTaskId: current.id }}\n onClose={() => { setShowSplit(false) }}\n />\n )}\n </div>\n )\n}\n","/**\n * 看板主视图(T004 §12.2 + T008 §12.8 过滤 + T009 §12.10 排序/分组/拖拽\n * + T011 §12.2/§12.7 待确认列与确认对话框 + T012 §12.9 需求拆分入口与批量确认):\n * 六列看板 + 过滤栏 + 搜索 + 归档视图 + 新建任务 + 详情 + 候选确认。\n *\n * - 顶部:返回会话、标题、Host 元信息(时区 + revision)、搜索过滤(标题/\n * 描述,大小写不敏感)、**排序视图模式切换(T009:手动顺序/按项目分组/按标签\n * 分组,会话级记忆、刷新重置,与过滤状态一致)**、归档视图切换(含计数)、\n * 新建任务按钮;\n * - 过滤栏(各列之上,T008 §12.8):项目下拉 + 标签多选 chips + 文本搜索;\n * 多条件 AND、同一条件内 OR;「清除全部」与命中计数;过滤跨列生效(含\n * 归档视图);过滤状态仅会话级记忆(刷新重置);\n * - Host 连接错误条 + 重试(transportError 由控制器在传输失败时置入);\n * - 六列:待确认/待规划/待办/进行中/已完成/已失败 + 各列计数;归档视图为单列;\n * - **待确认列(proposed,T011 §12.2)**:候选任务卡片带「待确认」角标与来源\n * 标记(对话/需求拆分);点击卡片进入确认对话框(可编辑、选落列、拒绝);\n * 候选任务不可拖拽;\n * - 卡片按任务粒度 memo 化(MemoTaskCard:稳定回调 + 控制器身份保持合并),\n * 兄弟卡片变化不引发全量重渲染;过滤只筛选不重建任务对象(filterTasks 保持\n * 对象引用),卡片 memo 边界不受过滤/分组切换影响;\n * - **排序/分组(T009 §12.10)**:手动顺序完全按 order 升序;按项目分组组头 =\n * 项目名(无项目归「未分类」,组内按 order);按标签分组任务按全部 tags 多归组\n * (无标签归「未分类」)。分组是视图层能力,不改底层 order/归属数据;\n * - **拖拽(T009 §12.10/§17 一致性)**:列内拖拽 = `reorder`(分组视图跨组落位\n * 携带归属变更与 order 原子落账);跨列拖拽 = `move`(backlog/todo 互移、\n * done/failed 拖回 backlog/todo 为重开不触发执行);running/proposed/归档\n * 任务不可拖拽,归档视图不参与拖拽;拖到 running 等非法目标被拒绝并提示。\n * 拖拽采用「本地 pending 渲染 + Host 确认落定、失败回滚」——UI 不先写未确认\n * 状态,pending 位置仅做展示,Host 拒绝/失败即回到原快照位置;\n * - 选中任务:proposed 候选 → 确认对话框(ConfirmTaskDialog);其他 → 详情\n * (TaskDetail);\n * - **需求拆分(T012 §12.9)**:顶部「从需求拆分」按钮 → RequirementSplitModal\n * (粘贴需求文本或工作区文件路径 → start-split,Host 独立拆分会话结算后父\n * 需求任务 + 批量候选进入待确认列);待确认列头部「批量确认」→\n * SplitConfirmDialog(逐条编辑/增删/批量确认/整体拒绝);确认后的子任务卡片\n * 带所属需求(父任务)徽标可跳转。\n */\nimport { memo, useCallback, useEffect, useRef, useState } from 'react'\nimport type { DragEvent, ReactNode } from 'react'\nimport type { TaskRecord, TaskStatus } from '../../core/model.ts'\nimport { selectedTaskOf, type BoardController } from '../board-controller.ts'\nimport {\n buildDropAction,\n isLegalDrop,\n visibleDropIndex,\n type DragOrigin,\n type DropTarget,\n} from '../drag-utils.ts'\nimport {\n applyOwnershipTransform,\n groupContains,\n groupKeyOf,\n groupTasks,\n GROUP_MODES,\n sortByOrder,\n UNGROUPED,\n type GroupMode,\n type TaskGroup,\n} from '../grouping.ts'\nimport {\n distinctProjects,\n distinctTags,\n EMPTY_FILTER,\n filterTasks,\n isFilterActive,\n type TaskFilter,\n} from '../filter.ts'\nimport { t, type NovaTaskBoardKey } from '../locales.ts'\nimport css from '../board.module.css'\nimport { ConfirmTaskDialog } from './ConfirmTaskDialog.tsx'\nimport { NewTaskModal } from './NewTaskModal.tsx'\nimport { RequirementSplitModal } from './RequirementSplitModal.tsx'\nimport { SplitConfirmDialog } from './SplitConfirmDialog.tsx'\nimport { STATUS_KEY } from './status-key.ts'\nimport { TaskCard } from './TaskCard.tsx'\nimport { TaskDetail } from './TaskDetail.tsx'\n\n/** 六列(P3.1/T011 起含 proposed 待确认列,§12.2)。 */\nconst COLUMNS: readonly { status: 'proposed' | 'backlog' | 'todo' | 'running' | 'done' | 'failed' }[] = [\n { status: 'proposed' },\n { status: 'backlog' },\n { status: 'todo' },\n { status: 'running' },\n { status: 'done' },\n { status: 'failed' },\n]\n\n/** 排序视图模式 → 顶部切换按钮文案 key。 */\nconst GROUP_MODE_KEYS: Record<GroupMode, NovaTaskBoardKey> = {\n manual: 'board.group.manual',\n project: 'board.group.project',\n tag: 'board.group.tag',\n}\n\n/** 非法落点提示的暂留时长(ms):拖拽拒绝后提示停留,随后自动消失。 */\nconst DRAG_REJECT_HINT_MS = 2500\n\n/**\n * Memoized per-card adapter:`onOpen` 等回调稳定 + 未变化任务行对象引用保持\n * (控制器身份合并),卡片只在自身任务/pending/拖拽状态变化时重渲染。`groupKey`\n * 为分组视图下该卡片所在组(手动模式 undefined;标签模式同一任务可在多个组各\n * 渲染一张实例,各实例携带自己的组上下文)。\n */\nconst MemoTaskCard = memo(function MemoTaskCard({ task, pending, timeZone, onOpen, groupKey, draggable, dragging, onDragStart, onDragEnd, onDragOverCard, onDropCard, parentId, parentTitle, onOpenParent }: {\n task: TaskRecord\n pending: boolean\n timeZone?: string\n onOpen: (id: string) => void\n groupKey?: string\n draggable: boolean\n dragging: boolean\n onDragStart: (task: TaskRecord, groupKey?: string) => void\n onDragEnd: () => void\n onDragOverCard: (task: TaskRecord, groupKey: string | undefined, event: DragEvent<HTMLElement>) => void\n onDropCard: (task: TaskRecord, groupKey: string | undefined, event: DragEvent<HTMLElement>) => void\n parentId?: string\n parentTitle?: string\n onOpenParent: (id: string) => void\n}) {\n const onClick = useCallback(() => { onOpen(task.id) }, [task.id, onOpen])\n return (\n <TaskCard\n task={task}\n pending={pending}\n timeZone={timeZone}\n onClick={onClick}\n draggable={draggable}\n dragging={dragging}\n onDragStart={dragged => { onDragStart(dragged, groupKey) }}\n onDragEnd={onDragEnd}\n onDragOverCard={(dragged, event) => { onDragOverCard(dragged, groupKey, event) }}\n onDropCard={(dragged, event) => { onDropCard(dragged, groupKey, event) }}\n parentId={parentId}\n parentTitle={parentTitle}\n onOpenParent={onOpenParent}\n />\n )\n})\n\n/** 落位目标是否与上次悬停等价(避免 dragover 高频 setState 触发全量重渲染)。 */\nfunction sameTarget(a: DropTarget | undefined, b: DropTarget): boolean {\n return a !== undefined && a.status === b.status && a.groupKey === b.groupKey\n && a.anchorId === b.anchorId && a.position === b.position\n}\n\n/** 拖拽 pending 渲染态(本地 pending 位置仅做展示,Host 确认后由快照接管)。 */\ninterface PendingDrag {\n taskId: string\n toStatus: TaskStatus\n groupKey?: string\n /** 目标列/目标组可见列表(剔除拖拽卡片后)内的落位下标。 */\n visibleIndex: number\n}\n\n/**\n * 看板顶部过滤栏(§12.8):项目下拉 + 标签多选 chips + 「清除全部」+ 命中计数。\n * 过滤跨列生效(含归档视图,调用方先筛视图再调用 filterTasks);多条件 AND、\n * 同一条件内 OR(标签命中任一);选项由当前视图任务集推导(distinctProjects/\n * distinctTags)。项目下拉含「全部项目」「未分类(无项目)」与各项目名。\n * 命中计数 = 过滤后的任务数(`hitCount`),仅在过滤激活时展示。\n */\nfunction FilterBar({ tasks, filter, hitCount, onChange }: {\n tasks: readonly TaskRecord[]\n filter: TaskFilter\n hitCount: number\n onChange: (filter: TaskFilter) => void\n}) {\n const projects = distinctProjects(tasks)\n const tags = distinctTags(tasks)\n const active = isFilterActive(filter)\n const projectValue = filter.project.kind === 'all' ? '' : filter.project.kind === 'none' ? '__none__' : filter.project.name\n const toggleTag = (tag: string): void => {\n const selected = filter.tags.includes(tag)\n onChange({ ...filter, tags: selected ? filter.tags.filter(candidate => candidate !== tag) : [...filter.tags, tag] })\n }\n return (\n <div className={css.filterBar} data-dsh-part=\"filter-bar\">\n <label className={css.filterItem}>\n <span className={css.filterLabel}>{t('filter.project')}</span>\n <select\n className={css.select}\n value={projectValue}\n aria-label={t('filter.project')}\n onChange={event => {\n const value = event.target.value\n onChange({\n ...filter,\n project: value === '' ? { kind: 'all' } : value === '__none__' ? { kind: 'none' } : { kind: 'name', name: value },\n })\n }}\n >\n <option value=\"\">{t('filter.projectAll')}</option>\n <option value=\"__none__\">{t('filter.projectNone')}</option>\n {projects.map(project => (\n <option key={project} value={project}>{project}</option>\n ))}\n </select>\n </label>\n {tags.length > 0 && (\n <span className={css.filterItem}>\n <span className={css.filterLabel}>{t('filter.tags')}</span>\n <span className={css.filterChips}>\n {tags.map(tag => (\n <button\n key={tag}\n type=\"button\"\n className={`${css.filterChip}${filter.tags.includes(tag) ? ` ${css.filterChipActive}` : ''}`}\n aria-pressed={filter.tags.includes(tag)}\n onClick={() => { toggleTag(tag) }}\n >\n {tag}\n </button>\n ))}\n </span>\n </span>\n )}\n <span className={css.filterActions}>\n {active && (\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { onChange(EMPTY_FILTER) }}\n >\n {t('filter.clearAll')}\n </button>\n )}\n {active && <span className={css.filterHitCount}>{t('filter.hits', { count: String(hitCount) })}</span>}\n </span>\n </div>\n )\n}\n\n/**\n * 看板组件;订阅控制器快照渲染。拖拽状态(drag/hover/pending/dragError)为\n * 组件内会话级状态:drag = 拖拽来源、hover = 当前落点(指示条)、pending =\n * 提交在途的本地占位渲染(Host 确认后清除)、dragError = 非法落点提示。\n */\nexport function TaskBoard({ controller }: { controller: BoardController }) {\n const [snapshot, setSnapshot] = useState(controller.getSnapshot())\n useEffect(\n () => controller.subscribe(() => setSnapshot(controller.getSnapshot())),\n [controller],\n )\n const [filter, setFilter] = useState<TaskFilter>(EMPTY_FILTER)\n const [showNew, setShowNew] = useState(false)\n const [showSplit, setShowSplit] = useState(false)\n const [showBatch, setShowBatch] = useState(false)\n const [drag, setDrag] = useState<DragOrigin | undefined>(undefined)\n const [hover, setHover] = useState<DropTarget | undefined>(undefined)\n const [pending, setPending] = useState<PendingDrag | undefined>(undefined)\n const [dragError, setDragError] = useState<string | undefined>(undefined)\n const dragErrorTimer = useRef<number | undefined>(undefined)\n const selected = selectedTaskOf(snapshot)\n const archiveView = snapshot.archiveView\n const groupMode = snapshot.groupMode\n const archiveCount = snapshot.tasks.filter(task => task.archivedAt !== undefined).length\n const viewTasks = snapshot.tasks.filter(task =>\n archiveView ? task.archivedAt !== undefined : task.archivedAt === undefined)\n const visible = filterTasks(viewTasks, filter)\n // 最新任务集/可见任务集经 ref 暴露给拖拽回调:commitDrop 等回调保持稳定引用,\n // 兄弟任务变化不会改写回调身份、不破坏卡片 memo 边界(§12.2/§17 性能)。\n const tasksRef = useRef(snapshot.tasks)\n tasksRef.current = snapshot.tasks\n const visibleRef = useRef(visible)\n visibleRef.current = visible\n const openTask = useCallback((id: string): void => { controller.openTask(id) }, [controller])\n const timeZone = snapshot.host?.scheduler.timeZone\n\n // --- 拖拽事件(T009 §12.10)-------------------------------------------------\n\n /** 展示非法落点提示并安排自动消失(悬停拒绝/落点拒绝共用)。 */\n const showDragError = useCallback((message: string) => {\n setDragError(message)\n if (dragErrorTimer.current !== undefined) window.clearTimeout(dragErrorTimer.current)\n dragErrorTimer.current = window.setTimeout(() => {\n dragErrorTimer.current = undefined\n setDragError(undefined)\n }, DRAG_REJECT_HINT_MS)\n }, [])\n\n const handleCardDragStart = useCallback((task: TaskRecord, groupKey?: string) => {\n if (dragErrorTimer.current !== undefined) {\n window.clearTimeout(dragErrorTimer.current)\n dragErrorTimer.current = undefined\n }\n setDragError(undefined)\n setHover(undefined)\n setDrag({\n taskId: task.id,\n fromStatus: task.status,\n fromTags: [...task.tags],\n ...(groupKey === undefined ? {} : { fromGroupKey: groupKey }),\n })\n }, [])\n\n const handleDragEnd = useCallback(() => {\n setDrag(undefined)\n setHover(undefined)\n }, [])\n\n /** 卡片悬停:按鼠标 Y 位置判定 before/after(阻止冒泡,避免列级覆盖为组尾/列尾)。 */\n const handleCardDragOver = useCallback((\n task: TaskRecord,\n groupKey: string | undefined,\n event: DragEvent<HTMLElement>,\n ) => {\n if (drag === undefined) return\n if (!isLegalDrop(drag, task.status)) return // 非法目标:交给列级统一拒绝\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n const rect = event.currentTarget.getBoundingClientRect()\n const before = event.clientY < rect.top + rect.height / 2\n setHover(prev => {\n const next: DropTarget = { status: task.status, groupKey, anchorId: task.id, position: before ? 'before' : 'after' }\n return sameTarget(prev, next) ? prev : next\n })\n }, [drag])\n\n /** 列悬停:列尾落位;非法目标(running 等)标记拒绝并提示。 */\n const handleColumnDragOver = useCallback((status: TaskStatus, event: DragEvent<HTMLElement>) => {\n if (drag === undefined) return\n event.preventDefault()\n const legal = isLegalDrop(drag, status)\n event.dataTransfer.dropEffect = legal ? 'move' : 'none'\n setHover(prev => {\n const next: DropTarget = { status, position: 'end' }\n return sameTarget(prev, next) ? prev : next\n })\n if (!legal) showDragError(t('board.dragRejected'))\n }, [drag, showDragError])\n\n /** 分组悬停:组尾落位;非法目标标记拒绝并提示。 */\n const handleGroupDragOver = useCallback((groupKey: string, status: TaskStatus, event: DragEvent<HTMLElement>) => {\n if (drag === undefined) return\n event.preventDefault()\n const legal = isLegalDrop(drag, status)\n event.dataTransfer.dropEffect = legal ? 'move' : 'none'\n setHover(prev => {\n const next: DropTarget = { status, groupKey, position: 'end' }\n return sameTarget(prev, next) ? prev : next\n })\n if (!legal) showDragError(t('board.dragRejected'))\n }, [drag, showDragError])\n\n /** 提交拖拽(§17 一致性:pending 只展示,以 Host 确认的 order/状态为准)。 */\n const commitDrop = useCallback(async (origin: DragOrigin, target: DropTarget) => {\n if (!isLegalDrop(origin, target.status)) {\n showDragError(t('board.dragRejected'))\n setDrag(undefined)\n setHover(undefined)\n return\n }\n const lastVisibleId = visibleRef.current.filter(task => task.status === target.status).at(-1)?.id\n const action = buildDropAction(origin, target, groupMode, tasksRef.current, lastVisibleId)\n if (action === undefined) {\n // 拖到自己/任务缺失:静默忽略,不提示。\n setDrag(undefined)\n setHover(undefined)\n return\n }\n // pending 占位下标:目标列/目标组可见列表(剔除拖拽卡片后)内。\n const visibleIndex = pendingVisibleIndex(origin, target, groupMode, target.status, visibleRef.current)\n setPending({ taskId: origin.taskId, toStatus: target.status, groupKey: target.groupKey, visibleIndex })\n setDragError(undefined)\n const confirmed = action.kind === 'reorder'\n ? await controller.reorderTask(action.taskId, action.status, action.order, {\n project: action.project,\n tags: action.tags,\n })\n : await controller.moveTask(action.taskId, action.status, action.order)\n void confirmed // Host 拒绝时控制器已置 transportError(错误条展示);pending 回滚到原快照位置。\n setPending(undefined)\n setDrag(undefined)\n setHover(undefined)\n }, [controller, groupMode, showDragError])\n\n const handleCardDrop = useCallback((\n task: TaskRecord,\n groupKey: string | undefined,\n event: DragEvent<HTMLElement>,\n ) => {\n event.preventDefault()\n event.stopPropagation()\n if (drag === undefined) return\n const target: DropTarget = hover !== undefined && hover.status === task.status && hover.anchorId === task.id\n ? hover\n : { status: task.status, groupKey, anchorId: task.id, position: 'after' }\n void commitDrop(drag, target)\n }, [drag, hover, commitDrop])\n\n const handleColumnDrop = useCallback((status: TaskStatus, event: DragEvent<HTMLElement>) => {\n event.preventDefault()\n if (drag === undefined) return\n let target: DropTarget\n if (groupMode !== 'manual') {\n // 分组视图下列级落位:落入卡片自身所属组(空列/组间空隙也能接收)。\n const card = tasksRef.current.find(candidate => candidate.id === drag.taskId)\n target = { status, groupKey: card === undefined ? undefined : groupKeyOf(card, groupMode), position: 'end' }\n } else {\n target = hover !== undefined && hover.status === status ? hover : { status, position: 'end' }\n }\n void commitDrop(drag, target)\n }, [drag, hover, groupMode, commitDrop])\n\n const handleGroupDrop = useCallback((groupKey: string, status: TaskStatus, event: DragEvent<HTMLElement>) => {\n event.preventDefault()\n if (drag === undefined) return\n const target: DropTarget = hover !== undefined && hover.status === status && hover.groupKey === groupKey\n ? hover\n : { status, groupKey, position: 'end' }\n void commitDrop(drag, target)\n }, [drag, hover, commitDrop])\n\n // --- 渲染辅助(T009 排序/分组/拖拽)-----------------------------------------\n\n /** 拖拽卡片(pending 渲染用;Host 未确认,任务行仍为原快照值)。 */\n const pendingCard = (taskId: string): TaskRecord | undefined =>\n snapshot.tasks.find(task => task.id === taskId)\n\n /** 落点指示条下标(当前渲染列表内);drag 未激活/悬停不匹配返回 undefined。 */\n const indicatorIndexIn = (list: readonly TaskRecord[], status: TaskStatus, groupKey: string | undefined): number | undefined => {\n if (drag === undefined || hover === undefined || hover.status !== status || hover.groupKey !== groupKey) return undefined\n if (hover.anchorId !== undefined) {\n const index = list.findIndex(task => task.id === hover.anchorId)\n if (index !== -1) return index + (hover.position === 'after' ? 1 : 0)\n }\n return list.length\n }\n\n const cardElement = (task: TaskRecord, groupKey: string | undefined): ReactNode => {\n const draggable = !archiveView && task.archivedAt === undefined\n && task.status !== 'running' && task.status !== 'proposed'\n const parent = task.parentId === undefined ? undefined\n : snapshot.tasks.find(candidate => candidate.id === task.parentId)\n return (\n <MemoTaskCard\n key={task.id}\n task={task}\n pending={snapshot.pendingTaskIds.includes(task.id)}\n timeZone={timeZone}\n onOpen={openTask}\n groupKey={groupKey}\n draggable={draggable}\n dragging={drag?.taskId === task.id}\n onDragStart={handleCardDragStart}\n onDragEnd={handleDragEnd}\n onDragOverCard={handleCardDragOver}\n onDropCard={handleCardDrop}\n parentId={parent === undefined ? undefined : parent.id}\n parentTitle={parent === undefined ? undefined : parent.title}\n onOpenParent={openTask}\n />\n )\n }\n\n /** 渲染一组卡片(含落点指示条;空列表且无指示条时展示空文案)。 */\n const renderCards = (\n list: readonly TaskRecord[],\n status: TaskStatus,\n groupKey: string | undefined,\n emptyLabel: string,\n ): ReactNode => {\n const indicator = indicatorIndexIn(list, status, groupKey)\n const items: ReactNode[] = []\n list.forEach((task, index) => {\n if (indicator === index) items.push(<div key={`indicator-${index}`} className={css.dropIndicator} data-dsh-part=\"drop-indicator\" />)\n items.push(cardElement(task, groupKey))\n })\n if (indicator === list.length) items.push(<div key=\"indicator-end\" className={css.dropIndicator} data-dsh-part=\"drop-indicator\" />)\n if (items.length === 0) return <div className={css.columnEmpty}>{emptyLabel}</div>\n return <>{items}</>\n }\n\n /** 手动顺序列(§12.10:完全按 order 升序;pending 时在目标位插入占位)。 */\n const renderManualColumn = (status: TaskStatus): ReactNode => {\n let list = visible.filter(task => task.status === status)\n if (pending !== undefined) {\n if (pending.toStatus === status) {\n const card = pendingCard(pending.taskId)\n if (card !== undefined) {\n list = list.filter(task => task.id !== pending.taskId)\n const index = Math.min(pending.visibleIndex, list.length)\n list = [...list.slice(0, index), applyOwnershipTransform(card, pending.groupKey, groupMode), ...list.slice(index)]\n }\n } else {\n list = list.filter(task => task.id !== pending.taskId)\n }\n }\n return renderCards(sortByOrder(list), status, undefined, t('board.empty'))\n }\n\n /** 分组列(§12.10:项目/标签分组,组内按 order;pending 时按目标组调整)。 */\n const renderGroupedColumn = (status: TaskStatus, mode: Exclude<GroupMode, 'manual'>): ReactNode => {\n let list = visible.filter(task => task.status === status)\n if (pending !== undefined && pending.toStatus !== status) {\n list = list.filter(task => task.id !== pending.taskId)\n }\n let groups: TaskGroup[] = groupTasks(list, mode)\n if (pending !== undefined && pending.toStatus === status) {\n const card = pendingCard(pending.taskId)\n if (card !== undefined) {\n const transformed = applyOwnershipTransform(card, pending.groupKey, groupMode)\n let matched = false\n groups = groups.map(group => {\n if (group.key === pending.groupKey) {\n matched = true\n const without = group.tasks.filter(task => task.id !== pending.taskId)\n const index = Math.min(pending.visibleIndex, without.length)\n return { ...group, tasks: [...without.slice(0, index), transformed, ...without.slice(index)] }\n }\n // 其他组:归属变换后仍属于该组则保持(标签模式多归组),否则移除占位。\n return groupContains(transformed, group.key, groupMode)\n ? group\n : { ...group, tasks: group.tasks.filter(task => task.id !== pending.taskId) }\n })\n if (!matched) {\n // 目标组当前为空(如空「未分类」组不渲染):临时补一组承载占位卡片。\n groups = [...groups, {\n key: pending.groupKey ?? UNGROUPED,\n label: pending.groupKey ?? '',\n tasks: [transformed],\n }]\n }\n }\n }\n if (groups.length === 0) return <div className={css.columnEmpty}>{t('board.empty')}</div>\n return (\n <>\n {groups.map(group => (\n <div\n key={group.key === UNGROUPED ? '__ungrouped__' : group.key}\n className={css.group}\n data-group={group.key === UNGROUPED ? 'ungrouped' : group.key}\n data-dsh-part=\"group\"\n data-drag-over={drag !== undefined && hover?.status === status && hover.groupKey === group.key ? '' : undefined}\n data-drag-rejected={drag !== undefined && hover?.status === status && hover.groupKey === group.key && !isLegalDrop(drag, status) ? '' : undefined}\n onDragOver={event => { handleGroupDragOver(group.key, status, event) }}\n onDrop={event => { handleGroupDrop(group.key, status, event) }}\n >\n <header className={css.groupHeader} data-dsh-part=\"group-header\">\n <span className={css.groupTitle}>{group.key === UNGROUPED ? t('board.group.ungrouped') : group.label}</span>\n <span className={css.groupCount}>{group.tasks.length}</span>\n </header>\n <div className={css.groupCards}>\n {renderCards(group.tasks, status, group.key, t('board.empty'))}\n </div>\n </div>\n ))}\n </>\n )\n }\n\n // --- 视图 -------------------------------------------------------------------\n\n return (\n <div className={css.board} data-dsh-nova-taskboard-board=\"\" data-dsh-plugin=\"nova-task-board\">\n <header className={css.boardHeader}>\n <button\n type=\"button\"\n className={`${css.ghostButton} ${css.backButton}`}\n aria-label={t('board.close')}\n onClick={() => { controller.closeBoard() }}\n >\n <span aria-hidden=\"true\">‹</span>\n <span>{t('board.close')}</span>\n </button>\n <h2 className={css.boardTitle}>{t('board.title')}</h2>\n {snapshot.host !== undefined && (\n <span className={css.hostMeta}>\n {t('board.hostMeta', {\n revision: String(snapshot.host.revision),\n timeZone: snapshot.host.scheduler.timeZone,\n })}\n </span>\n )}\n <input\n className={css.search}\n type=\"search\"\n placeholder={t('board.search')}\n value={filter.query}\n onChange={event => { setFilter({ ...filter, query: event.target.value }) }}\n aria-label={t('board.search')}\n />\n {!archiveView && (\n <div className={css.groupToggle} role=\"group\" aria-label={t('board.group.label')}>\n {GROUP_MODES.map(mode => (\n <button\n key={mode}\n type=\"button\"\n className={`${css.groupToggleButton}${groupMode === mode ? ` ${css.groupToggleActive}` : ''}`}\n aria-pressed={groupMode === mode}\n onClick={() => { controller.setGroupMode(mode) }}\n >\n {t(GROUP_MODE_KEYS[mode])}\n </button>\n ))}\n </div>\n )}\n <button\n type=\"button\"\n className={archiveView ? css.primaryButton : css.ghostButton}\n onClick={() => { controller.toggleArchiveView() }}\n >\n {archiveView\n ? t('board.backToBoard')\n : t('board.archiveView', { count: String(archiveCount) })}\n </button>\n <button\n type=\"button\"\n className={css.primaryButton}\n onClick={() => { setShowNew(true) }}\n >\n + {t('board.new')}\n </button>\n {!archiveView && (\n <button\n type=\"button\"\n className={css.ghostButton}\n onClick={() => { setShowSplit(true) }}\n >\n {t('split.entry')}\n </button>\n )}\n </header>\n\n <FilterBar tasks={viewTasks} filter={filter} hitCount={visible.length} onChange={setFilter} />\n\n {snapshot.transportError !== undefined && (\n <div className={css.formError} role=\"alert\">\n {t('board.hostError', { error: snapshot.transportError })}{' '}\n <button type=\"button\" className={css.linkButton} onClick={() => { void controller.retryHostSync() }}>\n {t('board.retryHost')}\n </button>\n </div>\n )}\n {dragError !== undefined && (\n <div className={css.formError} role=\"alert\">{dragError}</div>\n )}\n\n <div className={css.columns}>\n {archiveView ? (\n <section className={css.column} data-status=\"archived\" data-dsh-part=\"column\">\n <header className={css.columnHeader}>\n <h3 className={css.columnTitle}>{t('board.archive')}</h3>\n <span className={css.columnCount}>{visible.length}</span>\n </header>\n <div className={css.cards}>\n {renderCards(sortByOrder(visible), 'backlog', undefined, t('archive.empty'))}\n </div>\n </section>\n ) : (\n COLUMNS.map(column => {\n const status = column.status\n const dragging = drag !== undefined\n const isOver = dragging && hover?.status === status\n return (\n <section\n key={status}\n className={css.column}\n data-status={status}\n data-dsh-part=\"column\"\n data-drag-over={isOver && isLegalDrop(drag, status) ? '' : undefined}\n data-drag-rejected={isOver && !isLegalDrop(drag, status) ? '' : undefined}\n onDragOver={event => { handleColumnDragOver(status, event) }}\n onDrop={event => { handleColumnDrop(status, event) }}\n >\n <header className={css.columnHeader}>\n <span className={css.statusDot} data-status={status} aria-hidden=\"true\" />\n <h3 className={css.columnTitle}>{t(STATUS_KEY[status])}</h3>\n <span className={css.columnCount}>\n {visible.filter(task => task.status === status).length}\n </span>\n {status === 'proposed' && visible.some(task => task.status === 'proposed') && (\n <button\n type=\"button\"\n className={css.linkButton}\n onClick={() => { setShowBatch(true) }}\n >\n {t('split.batchButton')}\n </button>\n )}\n </header>\n <div className={css.cards}>\n {groupMode === 'manual'\n ? renderManualColumn(status)\n : renderGroupedColumn(status, groupMode)}\n </div>\n </section>\n )\n })\n )}\n </div>\n\n {selected !== undefined && (selected.status === 'proposed'\n ? (\n <ConfirmTaskDialog\n controller={controller}\n task={selected}\n onClose={() => { controller.closeTask() }}\n />\n )\n : (\n <TaskDetail controller={controller} task={selected} />\n ))}\n {showNew && (\n <NewTaskModal\n controller={controller}\n onClose={() => { setShowNew(false) }}\n />\n )}\n {showSplit && (\n <RequirementSplitModal\n controller={controller}\n onClose={() => { setShowSplit(false) }}\n />\n )}\n {showBatch && (\n <SplitConfirmDialog\n controller={controller}\n tasks={snapshot.tasks.filter(task => task.status === 'proposed' && task.archivedAt === undefined)}\n onClose={() => { setShowBatch(false) }}\n />\n )}\n </div>\n )\n}\n\n/**\n * pending 占位下标(组件内闭包,见 commitDrop):目标列/目标组可见列表\n * (剔除拖拽卡片后)内的落位。手动模式 = 列可见列表;分组视图 = 目标组可见列表。\n */\nfunction pendingVisibleIndex(\n origin: DragOrigin,\n target: DropTarget,\n mode: GroupMode,\n status: TaskStatus,\n visible: readonly TaskRecord[],\n): number {\n const columnVisible = visible.filter(task => task.status === status)\n if (mode === 'manual') {\n return visibleDropIndex(\n sortByOrder(columnVisible).filter(task => task.id !== origin.taskId),\n target,\n )\n }\n const group = groupTasks(columnVisible, mode).find(candidate => candidate.key === target.groupKey)\n return visibleDropIndex(\n (group?.tasks ?? []).filter(task => task.id !== origin.taskId),\n target,\n )\n}\n","/**\n * Board view mounting (T004: 真实看板 UI).\n *\n * The `conversation` slot is single-occupant (ui-conversation) and external\n * plugins cannot declare slots, so the board takes over the center column at\n * the DOM level: a container is appended inside the center column\n * (`[class*=\"centerCol\"]`, previously `[data-pane=\"conversation\"]` on older\n * shells) as an extra trailing child React never manages, and a stylesheet\n * rule hides the conversation content while the board is active. Toggling is\n * a data attribute on <html> — no React involvement, so the conversation\n * subtree underneath stays mounted and stateful.\n *\n * 属性用 data-dsh-nova-taskboard-* 前缀(与参考实现 data-dsh-taskboard-*\n * 区分,AGENTS.md D3),且联动既有跨插件激活事件 dsh-panel-activate。\n */\nimport { createRoot, type Root } from 'react-dom/client'\nimport type { BoardController } from './board-controller.ts'\nimport { TaskBoard } from './board/TaskBoard.tsx'\nimport css from './board.module.css'\n\n/** The injected board container (kept in the DOM, hidden when inactive). */\nexport const BOARD_VIEW_SELECTOR = '[data-dsh-nova-taskboard-view]'\n\nconst CONVERSATION_COLUMN_SELECTOR = '[data-pane=\"conversation\"], [class*=\"centerCol\"]'\nconst ACTIVE_ATTR = 'data-dsh-nova-taskboard-active'\n/** Sibling panels' activation attributes, removed when this panel opens. */\nconst OTHER_ACTIVE_ATTRS = ['data-dsh-taskboard-active', 'data-dsh-ssh-active']\n/** Cross-plugin activation event; detail is the activating panel name. */\nconst ACTIVATE_EVENT = 'dsh-panel-activate'\nconst PANEL_NAME = 'nova-taskboard'\n\n/** Find the center column, or undefined while the frame is not mounted. */\nfunction conversationColumn(): HTMLElement | undefined {\n return document.querySelector<HTMLElement>(CONVERSATION_COLUMN_SELECTOR) ?? undefined\n}\n\n/**\n * Mount the board React tree into the center column and bind its\n * visibility to the controller's boardOpen state.\n * @param controller - the board controller driving the view.\n * @returns disposer unmounting the tree and restoring the column.\n */\nexport function mountBoard(controller: BoardController): () => void {\n let root: Root | undefined\n let container: HTMLDivElement | undefined\n\n const ensure = (): void => {\n if (container !== undefined) return\n const column = conversationColumn()\n if (column === undefined) return\n container = document.createElement('div')\n container.dataset.dshNovaTaskboardView = ''\n container.dataset.dshPlugin = 'nova-task-board'\n container.className = css.boardView\n column.appendChild(container)\n root = createRoot(container)\n root.render(<TaskBoard controller={controller} />)\n }\n\n // The frame mounts after boot settlement; watch for the column's arrival.\n const waitObserver = new MutationObserver(() => { ensure() })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const applyActive = (): void => {\n if (controller.getSnapshot().boardOpen) {\n // Single-occupant center column: opening this panel must evict sibling\n // panels (reference task board / ssh), both their html attributes and\n // their controller state, otherwise the panels' visibility rules fight.\n for (const attr of OTHER_ACTIVE_ATTRS) document.documentElement.removeAttribute(attr)\n document.documentElement.setAttribute(ACTIVE_ATTR, '')\n document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))\n } else {\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n }\n }\n const onOtherActivate = (event: Event): void => {\n const detail = (event as CustomEvent).detail\n if (detail !== PANEL_NAME && controller.getSnapshot().boardOpen) {\n controller.closeBoard()\n }\n }\n // Jump out on sidebar context clicks: clicking a session/workspace row\n // (including the already-current one) hands the center column back to the\n // conversation. Capture phase, so the panel closes before the shell\n // processes the click.\n const SIDEBAR_ROW_SELECTOR = '[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]'\n const onClickSidebarRow = (event: MouseEvent): void => {\n if (!controller.getSnapshot().boardOpen) return\n const target = event.target as HTMLElement | null\n if (target === null) return\n if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.closeBoard()\n }\n document.addEventListener('click', onClickSidebarRow, true)\n document.addEventListener(ACTIVATE_EVENT, onOtherActivate)\n const unsubscribe = controller.subscribe(applyActive)\n applyActive()\n ensure()\n\n return () => {\n document.removeEventListener('click', onClickSidebarRow, true)\n document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)\n waitObserver.disconnect()\n unsubscribe()\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n root?.unmount()\n root = undefined\n container?.remove()\n container = undefined\n }\n}\n","/**\n * 「转为任务」对话框(T011 §12.7 用户手动兜底):会话消息 hover「转为任务」\n * 按钮打开的提取表单。预填标题/描述(引用消息正文,可编辑),两条提交路径:\n *\n * - **创建为候选**(propose):进入看板「待确认」列(proposed,source=\n * conversation + 来源会话/消息引用),确认后方可执行——防注入闸门(§14.4);\n * - **直接创建**(create):按所选落列(待规划/待办)直接创建正式任务\n * (§12.7「或直接选择落列创建」)。\n *\n * 经 Host 确认(propose/create action 返回新快照、任务可见)后才关闭;失败\n * 展示错误并可重试(UI 不先写未确认状态)。\n */\nimport { useState } from 'react'\nimport type { BoardController } from '../board-controller.ts'\nimport { t } from '../locales.ts'\nimport css from '../board.module.css'\n\n/** 转任务表单的预填内容(由 chat-integration 从消息提取)。 */\nexport interface ConvertTaskPrefill {\n title: string\n description: string\n prompt: string\n /** 来源会话 id(propose 时写入 metadata,可跳回对话)。 */\n sourceConversationId?: string\n /** 来源消息 id(propose 时写入 metadata)。 */\n sourceMessageId?: string\n}\n\n/** 直接创建的落列选择(§12.7:提交为候选或直接选择落列创建)。 */\nconst DIRECT_TARGETS: readonly { target: 'backlog' | 'todo'; label: string }[] = [\n { target: 'backlog', label: t('convert.directBacklog') },\n { target: 'todo', label: t('convert.directTodo') },\n]\n\n/** 转为任务表单浮层。 */\nexport function ConvertTaskModal({ controller, prefill, onClose }: {\n controller: BoardController\n prefill: ConvertTaskPrefill\n onClose: () => void\n}) {\n const [title, setTitle] = useState(prefill.title)\n const [description, setDescription] = useState(prefill.description)\n const [prompt, setPrompt] = useState(prefill.prompt)\n const [direct, setDirect] = useState(false)\n const [target, setTarget] = useState<'backlog' | 'todo'>('todo')\n const [error, setError] = useState<string | undefined>(undefined)\n const [pending, setPending] = useState(false)\n\n const submit = async (propose: boolean): Promise<void> => {\n if (title.trim() === '') {\n setError(t('new.required'))\n return\n }\n setError(undefined)\n setPending(true)\n try {\n if (propose) {\n const created = await controller.proposeTask({\n title,\n description,\n prompt,\n sourceConversationId: prefill.sourceConversationId,\n sourceMessageId: prefill.sourceMessageId,\n })\n if (created === undefined) {\n setError(controller.getSnapshot().transportError ?? t('board.hostError', { error: 'propose failed' }))\n return\n }\n } else {\n const created = await controller.createTaskConfirmed({ title, description, prompt })\n if (created === undefined) {\n setError(controller.getSnapshot().transportError ?? t('board.hostError', { error: 'create failed' }))\n return\n }\n if (target !== 'todo') controller.moveTask(created.id, target)\n }\n onClose()\n } finally {\n setPending(false)\n }\n }\n\n return (\n <div className={css.modalBackdrop} onMouseDown={event => { if (event.target === event.currentTarget) onClose() }}>\n <form\n className={css.modal}\n role=\"dialog\"\n aria-label={t('convert.title')}\n onSubmit={event => { event.preventDefault(); void submit(!direct) }}\n >\n <h2 className={css.modalTitle}>{t('convert.title')}</h2>\n <p className={css.detailText}>{t('convert.fromConversation')}</p>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.title')}</span>\n <input\n className={css.input}\n value={title}\n autoFocus\n placeholder={t('new.titlePlaceholder')}\n onChange={event => { setTitle(event.target.value); setError(undefined) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.description')}</span>\n <textarea\n className={css.input}\n rows={3}\n value={description}\n placeholder={t('new.descriptionPlaceholder')}\n onChange={event => { setDescription(event.target.value) }}\n />\n </label>\n\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('new.prompt')}</span>\n <textarea\n className={css.input}\n rows={4}\n value={prompt}\n placeholder={t('new.promptPlaceholder')}\n onChange={event => { setPrompt(event.target.value) }}\n />\n </label>\n\n {direct && (\n <label className={css.field}>\n <span className={css.fieldLabel}>{t('convert.directTarget')}</span>\n <div className={css.confirmTargetRow} role=\"radiogroup\" aria-label={t('convert.directTarget')}>\n {DIRECT_TARGETS.map(option => (\n <button\n key={option.target}\n type=\"button\"\n role=\"radio\"\n aria-checked={target === option.target}\n className={`${css.confirmTargetButton}${target === option.target ? ` ${css.confirmTargetActive}` : ''}`}\n onClick={() => { setTarget(option.target) }}\n >\n {option.label}\n </button>\n ))}\n </div>\n </label>\n )}\n\n {!direct && <p className={css.detailText}>{t('convert.proposedHint')}</p>}\n\n {error !== undefined && <p className={css.formError} role=\"alert\">{error}</p>}\n\n <footer className={css.modalFooter}>\n <button type=\"button\" className={css.ghostButton} onClick={() => { setDirect(!direct) }}>\n {direct ? t('convert.submitProposed') : t('convert.submitDirect')}\n </button>\n <button type=\"button\" className={css.ghostButton} onClick={onClose}>\n {t('new.cancel')}\n </button>\n <button type=\"submit\" className={css.primaryButton} disabled={pending}>\n {pending ? t('board.pending') : (direct ? t('convert.submitDirect') : t('convert.submitProposed'))}\n </button>\n </footer>\n </form>\n </div>\n )\n}\n","/**\n * 对话候选任务提取集成(T011 §12.7/§14.4,B7 调研定案)。\n *\n * 两条提取入口:\n * 1. **Agent 建议(半自动)**:订阅当前会话的对话快照(sessions.binding →\n * SessionFace 的 ObservableSnapshot<ConversationSnapshot>),扫描已结算的\n * assistant 节点文本块中的结构化候选标记 `⟦task-board:propose⟧ 标题 | 描述 |\n * prompt`,逐条经 `propose` action 创建 proposed 候选(source=conversation\n * + 来源会话/消息引用);按 (sessionId, messageId|seq, 序号) 去重,避免会话\n * 快照重放/重订阅重复建候选。标记格式由 announceToAgent 系统提示播报指引\n * agent 输出(§12.5/§14.4)。\n * 2. **用户手动(兜底)**:向会话消息行(DOM 行 `[data-chat-anchor-key]`,键格式\n * 为 DSH `conversationContextKey` 的 `${len}:${kind}${id}`)注入 hover「转为\n * 任务」按钮;点击打开 ConvertTaskModal(预填消息正文,可编辑),提交为候选或\n * 直接选择落列创建。注入自愈:MutationObserver 持续扫描,React 重渲染重建的\n * 行会被重新注入(与 sidebar-entry 同机制,永不破坏宿主 DOM)。\n *\n * DSH 消息结构(B7 调研):\n * - 消息节点:`ConversationNode` 判别联合(user/assistant/steering/context/…);\n * user 节点 `content: ContentBlock[]`(type='text' 取文本)、assistant 节点\n * `blocks: AssistantBlock[]`(kind='text' 取文本)+ `messageId?`/`seq`;\n * - 节点上下文键:`conversationContextKey(kind, id)` = `${kind.length}:${kind}${id}`;\n * 输入消息(user/steering)的 kind 为 `input-message`,id 为消息 id——DOM 行\n * `data-chat-anchor-key` 据此识别「可转为任务」的行。\n */\nimport type {\n AssistantMessageNode,\n ConversationNode,\n ConversationSnapshot,\n SessionFace,\n UserMessageNode,\n} from '@deepseek-ai/dsh-client-runtime/client'\nimport { createRoot, type Root } from 'react-dom/client'\nimport type { BoardController } from './board-controller.ts'\nimport { t } from './locales.ts'\nimport css from './board.module.css'\nimport { ConvertTaskModal, type ConvertTaskPrefill } from './board/ConvertTaskModal.tsx'\n\n/** 候选标记(B7 定案:系统提示指引 agent 输出的结构化标记)。 */\nexport const PROPOSE_MARKER = '⟦task-board:propose⟧'\n\n/** 输入消息节点定义 kind(user/steering 共用;DOM 行据此识别「转为任务」)。 */\nexport const INPUT_MESSAGE_KIND = 'input-message'\n\n/** 解析出的一条候选标记内容。 */\nexport interface ProposeMarkerContent {\n title: string\n description: string\n prompt: string\n}\n\nconst MARKER_RE = /⟦task-board:propose⟧([^\\n]*)/g\n\n/**\n * 解析文本中的候选标记(B7 定案格式 `⟦task-board:propose⟧ 标题 | 描述 | prompt`)。\n * 一条文本可含多个标记(全局扫描,每个标记占据所在行);标记内容按 `|` 分段:\n * 首段 = 标题(空则整条丢弃)、次段 = 描述、其余拼接为 prompt。纯函数,可脱离\n * 宿主单测。\n */\nexport function parseProposeMarkers(text: string): ProposeMarkerContent[] {\n const results: ProposeMarkerContent[] = []\n MARKER_RE.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = MARKER_RE.exec(text)) !== null) {\n const parts = match[1].split('|').map(part => part.trim())\n const title = parts[0] ?? ''\n if (title === '') continue\n const description = parts[1] ?? ''\n const prompt = parts.slice(2).join('|').trim()\n results.push({ title, description, prompt })\n }\n return results\n}\n\n/**\n * 解析会话节点上下文键(B7:`${len}:${kind}${id}`,conversationContextKey 格式)。\n * 长度前缀不匹配 / 结构非法返回 undefined。\n */\nexport function parseContextKey(key: string): { kind: string; id: string } | undefined {\n const colon = key.indexOf(':')\n if (colon <= 0) return undefined\n const length = Number(key.slice(0, colon))\n if (!Number.isInteger(length) || length <= 0) return undefined\n const kind = key.slice(colon + 1, colon + 1 + length)\n if (kind.length !== length) return undefined\n return { kind, id: key.slice(colon + 1 + length) }\n}\n\n/** 从 user 节点内容块提取文本(ContentBlock type='text')。 */\nexport function userMessageText(node: UserMessageNode): string {\n let text = ''\n for (const block of node.content) {\n const candidate = block as { type?: string; text?: string }\n if (candidate.type === 'text' && typeof candidate.text === 'string') {\n if (text !== '') text += '\\n'\n text += candidate.text\n }\n }\n return text.trim()\n}\n\n/** 从 assistant 节点块提取文本(AssistantBlock kind='text')。 */\nexport function assistantMessageText(node: AssistantMessageNode): string {\n let text = ''\n for (const block of node.blocks) {\n if (block.kind !== 'text') continue\n const candidate = block as { text?: string }\n if (typeof candidate.text === 'string') {\n if (text !== '') text += '\\n'\n text += candidate.text\n }\n }\n return text.trim()\n}\n\n/** 会话列表 + 会话绑定的最小结构面(生产传 ctx.sessions,测试注入 fake)。 */\nexport interface ChatSessionsFace {\n list: {\n getSnapshot(): { current?: string }\n subscribe(listener: () => void): () => void\n }\n binding(id: string): { session: ChatSessionFace } | undefined\n}\n\n/** 会话快照订阅面(生产 = SessionFace 的 ObservableSnapshot 半边)。 */\nexport interface ChatSessionFace {\n getSnapshot(): ConversationSnapshot\n subscribe(listener: () => void): () => void\n}\n\n/** 节点是否为 assistant(提取候选标记)。 */\nfunction asAssistant(node: ConversationNode): AssistantMessageNode | undefined {\n return node.kind === 'assistant' ? node : undefined\n}\n\n/**\n * 订阅当前会话,扫描 assistant 文本中的候选标记并自动 propose(§12.7 入口 1)。\n * 去重键 = `${sessionId}:${messageId ?? seq}:${序号}`——会话快照重放/切换会话\n * 重订阅不会重复建候选;同一 assistant 消息重复出现同一标记(如重发)视为新\n * 候选(用户可 dismiss)。\n * @returns disposer。\n */\nexport function mountChatToTaskBoard(controller: BoardController, sessions: ChatSessionsFace): () => void {\n let current: string | undefined\n let unsubscribeSession: (() => void) | undefined\n const proposedKeys = new Set<string>()\n\n const scanSnapshot = (sessionId: string, snapshot: ConversationSnapshot): void => {\n let index = 0\n for (const node of snapshot.nodes) {\n const assistant = asAssistant(node)\n if (assistant === undefined) continue\n const text = assistantMessageText(assistant)\n if (!text.includes(PROPOSE_MARKER)) continue\n for (const marker of parseProposeMarkers(text)) {\n const key = `${sessionId}:${assistant.messageId ?? String(assistant.seq)}:${index}`\n index += 1\n if (proposedKeys.has(key)) continue\n proposedKeys.add(key)\n void controller.proposeTask({\n title: marker.title,\n description: marker.description,\n prompt: marker.prompt,\n sourceConversationId: sessionId,\n sourceMessageId: assistant.messageId ?? String(assistant.seq),\n })\n }\n }\n }\n\n const subscribeSession = (sessionId: string): void => {\n if (unsubscribeSession !== undefined) {\n unsubscribeSession()\n unsubscribeSession = undefined\n }\n const binding = sessions.binding(sessionId)\n if (binding === undefined) return\n unsubscribeSession = binding.session.subscribe(() => {\n scanSnapshot(sessionId, binding.session.getSnapshot())\n })\n scanSnapshot(sessionId, binding.session.getSnapshot())\n }\n\n const syncCurrent = (): void => {\n const next = sessions.list.getSnapshot().current\n if (next === current) return\n current = next\n if (next === undefined) {\n if (unsubscribeSession !== undefined) {\n unsubscribeSession()\n unsubscribeSession = undefined\n }\n return\n }\n subscribeSession(next)\n }\n\n const unsubscribeList = sessions.list.subscribe(syncCurrent)\n syncCurrent()\n return () => {\n unsubscribeList()\n if (unsubscribeSession !== undefined) unsubscribeSession()\n proposedKeys.clear()\n }\n}\n\n// --- 手动「转为任务」hover 按钮(B7:注入 [data-chat-anchor-key] 行内)------------\n\n/** 注入按钮的稳定标识(幂等 + 自愈判定)。 */\nconst CONVERT_ATTR = 'data-dsh-nova-taskboard-convert'\n/** 会话消息列表容器(B7:DSH 对话 UI 的滚动容器)。 */\nconst CONVERSATION_LIST_SELECTOR = '[data-conversation-scroll]'\n/** 消息行(B7:每个渲染节点带节点上下文键)。 */\nconst MESSAGE_ROW_SELECTOR = '[data-chat-anchor-key]'\n\n/** 从消息行提取预填内容(优先会话快照节点文本,回退行内文本)。 */\nfunction prefillFromRow(\n row: HTMLElement,\n rawKey: string,\n snapshot: ConversationSnapshot | undefined,\n): ConvertTaskPrefill {\n // 优先按节点上下文键查会话快照节点(user/steering 的 content 文本),\n // 比读 DOM 更干净(不含操作按钮等非正文文本)。\n let nodeText: string | undefined\n if (snapshot !== undefined) {\n const view = snapshot.chat.nodes.get(rawKey)\n const data = view?.data as { kind?: string; content?: readonly { type?: string; text?: string }[] } | undefined\n if (data !== undefined && data.kind === 'user' && data.content !== undefined) {\n const parts: string[] = []\n for (const block of data.content) {\n if (block.type === 'text' && typeof block.text === 'string') parts.push(block.text)\n }\n const joined = parts.join('\\n').trim()\n if (joined !== '') nodeText = joined\n }\n }\n const text = (nodeText ?? row.textContent ?? '').trim()\n const parsed = parseContextKey(rawKey)\n return prefillFromText(text, parsed?.id)\n}\n\n/**\n * 由消息文本派生预填:标题 = 首行(封顶 120 字)、描述 = 全文(封顶 4000),\n * prompt 留空(由确认/编辑时填写)。\n */\nexport function prefillFromText(text: string, messageId?: string): ConvertTaskPrefill {\n const lines = text.split('\\n').map(line => line.trim()).filter(line => line !== '')\n const title = (lines[0] ?? '').slice(0, 120)\n return {\n title,\n description: text.slice(0, 4000),\n prompt: '',\n ...(messageId === undefined ? {} : { sourceMessageId: messageId }),\n }\n}\n\n// --- 手动入口挂载(B7:会话消息行 hover「转为任务」按钮)-------------------------\n\n/** 打开 ConvertTaskModal(瞬时 React 根挂到 body,关闭即卸载)。 */\nfunction openConvertModal(controller: BoardController, prefill: ConvertTaskPrefill): void {\n const container = document.createElement('div')\n container.dataset.dshNovaTaskboardConvertModal = ''\n document.body.appendChild(container)\n let root: Root | undefined\n const close = (): void => {\n root?.unmount()\n root = undefined\n container.remove()\n }\n root = createRoot(container)\n root.render(<ConvertTaskModal controller={controller} prefill={prefill} onClose={close} />)\n}\n\n/**\n * 向会话消息行注入 hover「转为任务」按钮(§12.7 入口 2,B7 定案)。\n *\n * - 扫描 `[data-conversation-scroll]` 容器内的 `[data-chat-anchor-key]` 行;\n * - 键解析出 kind = `input-message`(user/steering)的行才注入(注入按钮的\n * 稳定标识 data-dsh-nova-taskboard-convert 幂等);\n * - 自愈:MutationObserver 持续扫描——React 重渲染重建的行重新注入,被宿主\n * 移除的按钮随行消失(不破坏宿主 DOM);\n * - 点击:取当前会话快照的节点文本(回退行文本)打开 ConvertTaskModal。\n * @returns disposer。\n */\nexport function mountMessageConvertButton(controller: BoardController, sessions: ChatSessionsFace): () => void {\n let list: HTMLElement | undefined\n let snapshot: ConversationSnapshot | undefined\n let current: string | undefined\n let unsubscribeSession: (() => void) | undefined\n\n const subscribeSession = (sessionId: string): void => {\n if (unsubscribeSession !== undefined) {\n unsubscribeSession()\n unsubscribeSession = undefined\n }\n const binding = sessions.binding(sessionId)\n snapshot = binding?.session.getSnapshot()\n if (binding === undefined) return\n // ObservableSnapshot.subscribe 只通知不传参:变化后重读快照。\n unsubscribeSession = binding.session.subscribe(() => { snapshot = binding.session.getSnapshot() })\n }\n const syncCurrent = (): void => {\n const next = sessions.list.getSnapshot().current\n if (next === current) return\n current = next\n snapshot = undefined\n if (next !== undefined) subscribeSession(next)\n else if (unsubscribeSession !== undefined) {\n unsubscribeSession()\n unsubscribeSession = undefined\n }\n }\n\n const injectInto = (row: HTMLElement): void => {\n const rawKey = row.dataset.chatAnchorKey\n if (rawKey === undefined) return\n const parsed = parseContextKey(rawKey)\n if (parsed === undefined || parsed.kind !== INPUT_MESSAGE_KIND) return\n if (row.querySelector(`[${CONVERT_ATTR}]`) !== null) return\n // 行内绝对定位需要相对定位的父级;只对注入行做最小侵入(保留宿主原值语义)。\n if (getComputedStyle(row).position === 'static') row.style.position = 'relative'\n const button = document.createElement('button')\n button.type = 'button'\n button.className = css.messageAction\n button.setAttribute(CONVERT_ATTR, '')\n button.textContent = t('convert.title')\n button.addEventListener('click', (event) => {\n event.stopPropagation()\n openConvertModal(controller, prefillFromRow(row, rawKey, snapshot))\n })\n row.appendChild(button)\n }\n\n const scan = (): void => {\n if (list === undefined || !list.isConnected) return\n for (const row of list.querySelectorAll<HTMLElement>(MESSAGE_ROW_SELECTOR)) injectInto(row)\n }\n\n const ensureList = (): void => {\n const found = document.querySelector<HTMLElement>(CONVERSATION_LIST_SELECTOR)\n if (found === list) return\n list = found ?? undefined\n if (list !== undefined) {\n listObserver.observe(list, { childList: true, subtree: true })\n scan()\n }\n }\n\n const listObserver = new MutationObserver(() => { scan() })\n const bodyObserver = new MutationObserver(() => { ensureList() })\n bodyObserver.observe(document.body, { childList: true, subtree: true })\n ensureList()\n\n const unsubscribeList = sessions.list.subscribe(syncCurrent)\n syncCurrent()\n return () => {\n bodyObserver.disconnect()\n listObserver.disconnect()\n unsubscribeList()\n if (unsubscribeSession !== undefined) unsubscribeSession()\n snapshot = undefined\n }\n}\n","/**\n * 协议层(DSH-REQ-001 §11):严格版本化 Action 判别联合、全量快照与 SSE 事件\n * 帧类型、幂等信封解析。T003 交付协议基线:\n *\n * - 端点前缀 `/api/nova-task-board`(AGENTS.md D2:参考实现已占用\n * `/api/task-board`,本插件必须使用 nova- 前缀);\n * - Action 判别联合基线:`import / create / update / delete / move / archive /\n * restore`;T005 扩展 `run / rerun`(真实执行,§13.2);T006 扩展\n * `set-schedule`(cron 周期定时,§9.4/§11.2:enabled/cron patch,nextRunAt/\n * lastTriggeredAt 浏览器不可写);T007 扩展 `set-one-shot`(一次性计划,\n * §11.2:runAt 缺省或 0 = 取消,与 set-schedule 互斥 D6);T008 扩展\n * `create.input`/`update.patch` 支持 project/tags(§9.2/§12.8);T009 扩展\n * `reorder`(列内排序/分组落位,§11.2:status 须与当前状态一致、order 有限\n * 数字、可携带 project/tags 归属变更)与 `move.order`(跨列拖拽落位,缺省\n * 追加目标列末尾);T011 扩展 `propose`(对话提取/手动转任务 → proposed\n * 候选,§11.2/§14.4:输入沿用 NewTaskInput 校验 + sourceConversationId/\n * sourceMessageId 来源引用,命令类字段递归拒绝 §16.7)、`confirm`(确认候选\n * → 落 backlog/todo,确认是唯一 promote 路径)、`dismiss`(拒绝/忽略删除);\n * T012 扩展 `propose-batch`(批量候选,§11.2/§14.5:items 逐条沿用 propose\n * 校验 + parentId 引用校验,整批原子落账、单 revision、幂等指纹按整批计算、\n * 载荷上限对齐 import 2 MiB)与 `start-split`(需求拆分驱动,§11.2/§12.9/\n * §14.5:提交需求文本/文件路径,Host 启动独立拆分会话并在结算时落账);\n * - 严格校验:exact keys、类型、枚举、cron 合法性、有限时间戳;未知字段/未知\n * action 一律拒绝;import 载荷递归拒绝 `args/command/executable/powershell/\n * shell` 字段(防注入,§16.2/§19-9);\n * - 幂等(§11.3):requestId + SHA-256 action 指纹,缓存判定复用 T002\n * HostLedger 的持久化 recentRequests(本层负责信封解析,指纹由账本层计算)。\n *\n * T010 扩展(§9.5/§11.2/§14,P3 自动化与闭环):\n * - `add-comment`(评论,author/body/type)、`add-artifact`(产物,\n * type/title/url/contentRef)、`update-context`(上下文快照部分补丁,空串/\n * 空数组 = 清除,updatedAt 浏览器不可写);\n * - `upsert-automation`(自动化规则创建/替换;nextRunAt/lastTriggeredAt 为\n * Host 独占字段,浏览器不可写)、`delete-automation`、`run-automation`\n * (手动触发一次收集);\n * - snapshot 新增 `automationRules`(§9.5 automation_rules 集合)。\n *\n * 分层说明:import 的任务行做「字段级严格校验 + 未知字段剥离」——v1 迁移数据\n * 可能携带 `priority` 等历史字段(§9.5 列入 P4 metadata),整行拒绝会阻断迁移,\n * 但命令类字段(禁止集)与类型/枚举/时间戳错误整行拒绝;envelope/action 层则\n * 是 exact-keys 严格拒绝。\n */\n\nimport type {\n AutomationRule,\n AutomationSource,\n ArtifactType,\n BatchProposeItem,\n CommentType,\n ExecutionRecord,\n NewTaskInput,\n ProposeTaskInput,\n ScheduleRule,\n SchedulerSnapshot,\n SplitRequestInput,\n TaskRecord,\n TaskStatus,\n TaskUpdatePatch,\n} from './core/model.ts'\nimport {\n isFiniteNumber,\n isTaskPermission,\n isTaskSource,\n isTaskStatus,\n LEDGER_SCHEMA_VERSION,\n} from './core/model.ts'\nimport { isArtifactType, isAutomationSource, isCommentType } from './core/model.ts'\nimport { isValidCron } from './core/cron.ts'\nimport type { AddArtifactInput, AddCommentInput, AutomationRuleInput, ContextSnapshotPatch } from './core/transitions.ts'\n\n/** 本项目 API 前缀(AGENTS.md D2,2026-08-22 定值;参考实现占用 /api/task-board)。 */\nexport const TASK_BOARD_API_PREFIX = '/api/nova-task-board'\n\n/** 电源保护阶段(§11.1 snapshot 的 power 字段;保护实现随后续任务,本期仅类型+基础值)。 */\nexport type PowerPhase = 'disabled' | 'idle' | 'acquiring' | 'active' | 'error' | 'unsupported'\n\n/** 电源保护对外快照(对齐参考实现形状,保证 T004 UI 的类型契约稳定)。 */\nexport interface TaskBoardPowerSnapshot {\n /** 宿主平台。 */\n platform: string\n /** 电源保护阶段。 */\n phase: PowerPhase\n /** 保护是否启用(enabled 配置 + 平台支持)。 */\n enabled: boolean\n /** 正在运行的执行数。 */\n runningSessions: number\n /** 已武装的计划数。 */\n armedSchedules: number\n /** 会话状态是否已知(T005 起由 5s 会话轮询驱动;轮询失败降级 false)。 */\n sessionStateKnown: boolean\n /** 最近可见错误。 */\n lastError?: string\n}\n\n/** 全量状态快照(§11.1 `GET /state`)。 */\nexport interface TaskBoardSnapshot {\n schemaVersion: typeof LEDGER_SCHEMA_VERSION\n revision: number\n tasks: TaskRecord[]\n scheduler: SchedulerSnapshot\n power: TaskBoardPowerSnapshot\n /** 自动化收集规则(T010,§9.5 automation_rules)。 */\n automationRules: AutomationRule[]\n}\n\n/** SSE 事件帧(§11.1 `GET /events`):只推 revision/scheduler/power,绝不携带任务列表。 */\nexport interface TaskBoardEventPayload {\n revision: number\n scheduler: SchedulerSnapshot\n power: TaskBoardPowerSnapshot\n}\n\n/** Action 判别联合(§11.2 基线 + T005 执行扩展 + T006 定时扩展 + T009 排序/拖拽扩展 + T011 候选流转扩展)。 */\nexport type TaskBoardAction =\n | { kind: 'import'; sourceId: string; tasks: TaskRecord[] } // v1 一次性迁移\n | { kind: 'create'; id: string; input: NewTaskInput } // 新建(默认 todo)\n | { kind: 'update'; taskId: string; patch: TaskUpdatePatch } // 标题/描述/prompt/工作区/模式/权限\n | { kind: 'delete'; taskId: string }\n | { kind: 'move'; taskId: string; status: TaskStatus; order?: number } // 跨列移动/跨列拖拽:backlog/todo 互移;done/failed 重开;order 缺省追加目标列末尾(T009)\n | { kind: 'reorder'; taskId: string; status: TaskStatus; order: number; project?: string; tags?: string[] } // 列内排序/分组落位(T009):status 须与当前状态一致;order 有限数字;可携带归属变更(project 空串清除 / tags 整体替换)与 order 原子落账\n | { kind: 'archive'; taskId: string } // 仅 done/failed\n | { kind: 'restore'; taskId: string }\n | { kind: 'set-schedule'; taskId: string; patch: { enabled?: boolean; cron?: string } } // cron 周期定时(T006):设定/修改/关闭\n | { kind: 'set-one-shot'; taskId: string; patch: { runAt?: number } } // 一次性计划(T007 §9.4/§11.2):runAt 缺省或 0 = 取消,与 set-schedule 互斥(D6)\n | { kind: 'run'; taskId: string } // 真实执行(T005 §13.2):开执行记录 + 会话启动\n | { kind: 'rerun'; taskId: string } // 重新执行(T005):先落回 todo 再执行\n // T011 对话候选流转(§11.2/§14.4,P3.1):\n | { kind: 'propose'; id: string; input: ProposeTaskInput } // 对话提取/手动转任务 → 创建 proposed 候选(source=conversation)\n | { kind: 'confirm'; taskId: string; target: 'backlog' | 'todo' } // 确认候选 → 落列(confirm 是唯一 promote 路径)\n | { kind: 'dismiss'; taskId: string } // 拒绝/忽略候选(删除)\n // T012 需求拆分(§11.2/§12.9/§14.5,P3.2):\n | { kind: 'propose-batch'; id: string; items: BatchProposeItem[] } // 批量创建候选任务(需求拆分产物,原子落账、单 revision,见 D8/14.5)\n | { kind: 'start-split'; id: string; input: SplitRequestInput } // 需求拆分驱动:提交需求文本/文件路径,Host 启动独立拆分会话并结算落账(B8)\n | { kind: 'add-comment'; taskId: string; comment: { author?: string; body: string; type?: CommentType } } // 评论(T010 §9.5)\n | { kind: 'add-artifact'; taskId: string; artifact: { type: ArtifactType; title: string; url?: string; contentRef?: string } } // 产物(T010 §9.5)\n | { kind: 'update-context'; taskId: string; patch: ContextSnapshotPatch } // 上下文快照部分补丁(T010 §9.5)\n | { kind: 'upsert-automation'; rule: AutomationRuleInput } // 自动化规则创建/替换(T010 §9.5)\n | { kind: 'delete-automation'; ruleId: string } // 删除规则(T010)\n | { kind: 'run-automation'; ruleId: string } // 手动触发一次收集(T010 §14.2 兜底)\n\n/** 幂等 action 信封(§11.3):requestId 浏览器 uuid。 */\nexport interface TaskBoardActionEnvelope {\n requestId: string\n action: TaskBoardAction\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n}\n\nfunction exactKeys(value: Record<string, unknown>, allowed: readonly string[]): boolean {\n return Object.keys(value).every(key => allowed.includes(key))\n}\n\nfunction optionalString(value: unknown): boolean {\n return value === undefined || typeof value === 'string'\n}\n\nfunction optionalFiniteNumber(value: unknown): boolean {\n return value === undefined || (typeof value === 'number' && Number.isFinite(value))\n}\n\n/** import/外部载荷递归拒绝的命令类字段(§16.2/§19-9,大小写不敏感)。 */\nconst FORBIDDEN_IMPORT_FIELDS = new Set(['args', 'command', 'executable', 'powershell', 'shell'])\n\n/**\n * 载荷是否含命令类字段(§16.2/§19-9):对键名递归检查\n * `args/command/executable/powershell/shell`(大小写不敏感)。import 与\n * GitHub Webhook 外部载荷共用(T010 §14.3:自动收集内容不携带命令结构)。\n */\nexport function hasForbiddenCommandFields(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(hasForbiddenCommandFields)\n const row = record(value)\n if (row === undefined) return false\n return Object.entries(row).some(\n ([key, nested]) => FORBIDDEN_IMPORT_FIELDS.has(key.toLowerCase()) || hasForbiddenCommandFields(nested),\n )\n}\n\n/** import 执行记录的严格校验与规范化(§9.3)。 */\nfunction strictImportedExecution(value: unknown): ExecutionRecord | undefined {\n const entry = record(value)\n if (entry === undefined || typeof entry.id !== 'string' || entry.id === '') return undefined\n if (!isFiniteNumber(entry.startedAt)) return undefined\n if (!optionalString(entry.sessionId)) return undefined\n if (!optionalFiniteNumber(entry.endedAt)) return undefined\n if (entry.result !== undefined && !['succeeded', 'failed', 'cancelled'].includes(String(entry.result))) return undefined\n if (!optionalString(entry.error)) return undefined\n const execution: ExecutionRecord = { id: entry.id, startedAt: entry.startedAt }\n if (typeof entry.sessionId === 'string') execution.sessionId = entry.sessionId\n if (isFiniteNumber(entry.endedAt)) execution.endedAt = entry.endedAt\n if (['succeeded', 'failed', 'cancelled'].includes(String(entry.result))) {\n execution.result = entry.result as ExecutionRecord['result']\n }\n if (typeof entry.error === 'string') execution.error = entry.error\n return execution\n}\n\n/** import 执行安排(§9.4 判别联合)的严格校验:cron 语法合法(isValidCron)、时间戳有限。 */\nfunction strictImportedSchedule(value: unknown): ScheduleRule | undefined {\n const rule = record(value)\n if (rule === undefined) return undefined\n if (rule.kind === 'one-shot') {\n if (!isFiniteNumber(rule.runAt) || !optionalFiniteNumber(rule.firedAt)) return undefined\n const schedule: ScheduleRule = { kind: 'one-shot', runAt: rule.runAt }\n if (isFiniteNumber(rule.firedAt)) schedule.firedAt = rule.firedAt\n return schedule\n }\n if (rule.kind !== undefined && rule.kind !== 'cron') return undefined\n if (typeof rule.enabled !== 'boolean' || typeof rule.cron !== 'string') return undefined\n if (!isValidCron(rule.cron)) return undefined\n if (!optionalFiniteNumber(rule.nextRunAt) || !optionalFiniteNumber(rule.lastTriggeredAt)) return undefined\n const schedule: ScheduleRule = { kind: 'cron', enabled: rule.enabled, cron: rule.cron }\n if (isFiniteNumber(rule.nextRunAt)) schedule.nextRunAt = rule.nextRunAt\n if (isFiniteNumber(rule.lastTriggeredAt)) schedule.lastTriggeredAt = rule.lastTriggeredAt\n return schedule\n}\n\n/**\n * import 任务行的严格校验与规范化。校验失败(命令类字段、类型/枚举/时间戳/\n * cron 非法)整行拒绝;未知字段剥离(v1 历史字段如 priority 不阻断迁移)。\n */\nfunction strictImportedTask(value: unknown): TaskRecord | undefined {\n const input = record(value)\n if (input === undefined || hasForbiddenCommandFields(input)) return undefined\n if (typeof input.id !== 'string' || input.id === '') return undefined\n if (typeof input.title !== 'string' || typeof input.description !== 'string' || typeof input.prompt !== 'string') {\n return undefined\n }\n if (!isTaskStatus(input.status)) return undefined\n if (!isFiniteNumber(input.createdAt) || !isFiniteNumber(input.updatedAt)) return undefined\n if (input.source !== undefined && !isTaskSource(input.source)) return undefined\n if (!optionalString(input.workspaceId) || !optionalString(input.mode)) return undefined\n if (input.permission !== undefined && !isTaskPermission(input.permission)) return undefined\n if (input.archivedAt !== undefined && !isFiniteNumber(input.archivedAt)) return undefined\n if (input.tags !== undefined && (!Array.isArray(input.tags) || !input.tags.every(tag => typeof tag === 'string'))) {\n return undefined\n }\n if (input.project !== undefined && typeof input.project !== 'string') return undefined\n if (input.parentId !== undefined && typeof input.parentId !== 'string') return undefined\n if (input.order !== undefined && !isFiniteNumber(input.order)) return undefined\n if (!Array.isArray(input.executions)) return undefined\n const executions: ExecutionRecord[] = []\n for (const entry of input.executions) {\n const execution = strictImportedExecution(entry)\n if (execution === undefined) return undefined\n executions.push(execution)\n }\n if (input.schedule !== undefined) {\n const schedule = strictImportedSchedule(input.schedule)\n if (schedule === undefined) return undefined\n }\n const task: TaskRecord = {\n id: input.id,\n title: input.title,\n description: input.description,\n prompt: input.prompt,\n status: input.status,\n createdAt: input.createdAt,\n updatedAt: input.updatedAt,\n executions,\n tags: input.tags === undefined ? [] : [...(input.tags as string[])],\n comments: [],\n artifacts: [],\n order: input.order === undefined ? 0 : input.order,\n }\n if (typeof input.source === 'string' && isTaskSource(input.source)) task.source = input.source\n if (typeof input.workspaceId === 'string' && input.workspaceId.trim() !== '') task.workspaceId = input.workspaceId.trim()\n if (typeof input.mode === 'string' && input.mode.trim() !== '') task.mode = input.mode.trim()\n if (isTaskPermission(input.permission)) task.permission = input.permission\n if (isFiniteNumber(input.archivedAt)) task.archivedAt = input.archivedAt\n const schedule = strictImportedSchedule(input.schedule)\n if (schedule !== undefined) task.schedule = schedule\n if (typeof input.project === 'string' && input.project.trim() !== '') task.project = input.project.trim()\n if (typeof input.parentId === 'string' && input.parentId.trim() !== '') task.parentId = input.parentId.trim()\n return task\n}\n\n/**\n * create 输入的严格校验(§11.2:exact keys、类型、枚举;无 schedule——归 T006)。\n * T008 扩展:`project` 可选字符串、`tags` 可选字符串数组(元素必须是字符串,\n * 内容清洗/去重由 transitions 的 normalizeTags 落账层完成)。\n */\nfunction createInput(value: unknown): value is NewTaskInput {\n const input = record(value)\n if (input === undefined || !exactKeys(input, [\n 'title', 'description', 'prompt', 'workspaceId', 'mode', 'permission', 'project', 'tags',\n ])) {\n return false\n }\n if (typeof input.title !== 'string' || typeof input.description !== 'string' || typeof input.prompt !== 'string') {\n return false\n }\n if (!optionalString(input.workspaceId) || !optionalString(input.mode)) return false\n if (!optionalString(input.project)) return false\n if (input.tags !== undefined && (!Array.isArray(input.tags) || !input.tags.every(tag => typeof tag === 'string'))) {\n return false\n }\n return input.permission === undefined || isTaskPermission(input.permission)\n}\n\n/**\n * propose 输入的严格校验(§11.2/§16.7,T011):在 create 输入之上扩展\n * `sourceConversationId`/`sourceMessageId` 两个可选字符串(来源会话/消息引用,\n * 写入 metadata 供跳回对话追溯);**递归拒绝命令类字段**(§16.7 安全红线,\n * 与 import/Webhook 同一条红线——候选内容永不被解释为可执行结构)。\n * 标题/描述/prompt 的内容清洗(控制字符/长度)由状态机落账层完成\n * (transitions.applyProposeTask)。\n */\nfunction proposeInput(value: unknown): value is ProposeTaskInput {\n const input = record(value)\n if (input === undefined || !exactKeys(input, [\n 'title', 'description', 'prompt', 'workspaceId', 'mode', 'permission', 'project', 'tags',\n 'sourceConversationId', 'sourceMessageId',\n ])) {\n return false\n }\n if (hasForbiddenCommandFields(input)) return false\n if (typeof input.title !== 'string' || typeof input.description !== 'string' || typeof input.prompt !== 'string') {\n return false\n }\n if (!optionalString(input.workspaceId) || !optionalString(input.mode)) return false\n if (!optionalString(input.project)) return false\n if (!optionalString(input.sourceConversationId) || !optionalString(input.sourceMessageId)) return false\n if (input.tags !== undefined && (!Array.isArray(input.tags) || !input.tags.every(tag => typeof tag === 'string'))) {\n return false\n }\n return input.permission === undefined || isTaskPermission(input.permission)\n}\n\n/**\n * propose-batch 条目的严格校验(§11.2/§16.8,T012):在 propose 输入之上扩展\n * `parentId` 可选字符串(父需求任务引用;必须指向账本内已存在且非自身的任务、\n * 禁止环引用——引用合法性由状态机落账层校验,见 transitions.applyProposeBatch)。\n * 命令类字段递归拒绝(与 propose/import 同一条红线)。\n */\nfunction batchProposeItem(value: unknown): value is BatchProposeItem {\n const input = record(value)\n if (input === undefined || !exactKeys(input, [\n 'title', 'description', 'prompt', 'workspaceId', 'mode', 'permission', 'project', 'tags',\n 'sourceConversationId', 'sourceMessageId', 'parentId',\n ])) {\n return false\n }\n if (hasForbiddenCommandFields(input)) return false\n if (typeof input.title !== 'string' || typeof input.description !== 'string' || typeof input.prompt !== 'string') {\n return false\n }\n if (!optionalString(input.workspaceId) || !optionalString(input.mode)) return false\n if (!optionalString(input.project)) return false\n if (!optionalString(input.sourceConversationId) || !optionalString(input.sourceMessageId)) return false\n if (!optionalString(input.parentId)) return false\n if (input.tags !== undefined && (!Array.isArray(input.tags) || !input.tags.every(tag => typeof tag === 'string'))) {\n return false\n }\n return input.permission === undefined || isTaskPermission(input.permission)\n}\n\n/**\n * start-split 输入的严格校验(§11.2/§12.9,T012):标题必填非空;`text` 与\n * `filePath` 至少其一(需求来源);project/parentTaskId 可选字符串、tags 可选\n * 字符串数组。需求文本是发给 agent 的数据(同任务 prompt 语义),**不做命令类\n * 字段拒绝**——控制字符/长度清洗在拆分结算落账层(transitions/split)。\n */\nfunction splitRequestInput(value: unknown): value is SplitRequestInput {\n const input = record(value)\n if (input === undefined || !exactKeys(input, [\n 'title', 'text', 'filePath', 'project', 'tags', 'parentTaskId',\n ])) {\n return false\n }\n if (typeof input.title !== 'string' || input.title.trim() === '') return false\n if (!optionalString(input.text) || !optionalString(input.filePath)) return false\n if (!optionalString(input.project) || !optionalString(input.parentTaskId)) return false\n if (input.tags !== undefined && (!Array.isArray(input.tags) || !input.tags.every(tag => typeof tag === 'string'))) {\n return false\n }\n const text = typeof input.text === 'string' ? input.text.trim() : ''\n const filePath = typeof input.filePath === 'string' ? input.filePath.trim() : ''\n return text !== '' || filePath !== ''\n}\n\n/** update 补丁的严格校验(§11.2:可编辑字段全集;Host/runner 独占字段不在补丁面内)。T008:project/tags 并入补丁面。 */\nfunction updatePatch(value: unknown): value is TaskUpdatePatch {\n const patch = record(value)\n if (patch === undefined || !exactKeys(patch, [\n 'title', 'description', 'prompt', 'workspaceId', 'mode', 'permission', 'project', 'tags',\n ])) {\n return false\n }\n for (const key of ['title', 'description', 'prompt', 'workspaceId', 'mode', 'project'] as const) {\n if (!optionalString(patch[key])) return false\n }\n if (patch.tags !== undefined && (!Array.isArray(patch.tags) || !patch.tags.every(tag => typeof tag === 'string'))) {\n return false\n }\n return patch.permission === undefined || isTaskPermission(patch.permission)\n}\n\n/**\n * set-schedule 补丁的严格校验(§11.2:exact keys 仅 enabled/cron;cron 出现时\n * 必须语法合法;两者皆缺 = 空补丁拒绝)。nextRunAt/lastTriggeredAt 是 Host\n * 独占字段,不在补丁面内(§9.4,浏览器不可写)。\n */\nfunction schedulePatch(value: unknown): value is { enabled?: boolean; cron?: string } {\n const patch = record(value)\n if (patch === undefined || !exactKeys(patch, ['enabled', 'cron'])) return false\n if (patch.enabled !== undefined && typeof patch.enabled !== 'boolean') return false\n if (patch.cron !== undefined && (typeof patch.cron !== 'string' || !isValidCron(patch.cron))) return false\n return patch.enabled !== undefined || patch.cron !== undefined\n}\n\n/**\n * set-one-shot 补丁的严格校验(§11.2:exact keys 仅 runAt;出现时必须是有限\n * 毫秒时间戳)。空补丁合法——runAt 缺省或 0 = 取消(§9.4 未到时刻前可取消,\n * 与 set-schedule「空补丁拒绝」不同:cron 空补丁无意义,one-shot 空补丁即取消)。\n */\nfunction oneShotPatch(value: unknown): value is { runAt?: number } {\n const patch = record(value)\n if (patch === undefined || !exactKeys(patch, ['runAt'])) return false\n if (patch.runAt !== undefined && !isFiniteNumber(patch.runAt)) return false\n return true\n}\n\n/**\n * add-comment 输入的严格校验(§11.2:exact keys 仅 author/body/type)。\n * body 必填非空字符串;author/type 可选(author 字符串、type 枚举)。\n * 内容清洗(控制字符/长度)由状态机落账层完成(transitions.applyAddComment)。\n */\nfunction commentInput(value: unknown): value is AddCommentInput {\n const input = record(value)\n if (input === undefined || !exactKeys(input, ['author', 'body', 'type'])) return false\n if (typeof input.body !== 'string' || input.body.trim() === '') return false\n if (!optionalString(input.author)) return false\n if (input.type !== undefined && !isCommentType(input.type)) return false\n return true\n}\n\n/**\n * add-artifact 输入的严格校验(§11.2:exact keys 仅 type/title/url/contentRef)。\n * type 枚举、title 必填非空字符串、url/contentRef 可选字符串。\n */\nfunction artifactInput(value: unknown): value is AddArtifactInput {\n const input = record(value)\n if (input === undefined || !exactKeys(input, ['type', 'title', 'url', 'contentRef'])) return false\n if (input.type === undefined || !isArtifactType(input.type)) return false\n if (typeof input.title !== 'string' || input.title.trim() === '') return false\n if (!optionalString(input.url) || !optionalString(input.contentRef)) return false\n return true\n}\n\n/**\n * update-context 补丁的严格校验(§11.2:exact keys 仅六个可编辑字段;空补丁\n * 拒绝)。字符串字段可选字符串、数组字段可选字符串数组;updatedAt 是 Host\n * 独占字段,不在补丁面内。\n */\nfunction contextPatch(value: unknown): value is ContextSnapshotPatch {\n const patch = record(value)\n if (patch === undefined || !exactKeys(patch, [\n 'goal', 'keyDecisions', 'filePaths', 'lastAiSummary', 'latestUserFeedback', 'relatedLinks',\n ])) {\n return false\n }\n for (const key of ['goal', 'lastAiSummary', 'latestUserFeedback'] as const) {\n if (!optionalString(patch[key])) return false\n }\n for (const key of ['keyDecisions', 'filePaths', 'relatedLinks'] as const) {\n if (patch[key] !== undefined && (!Array.isArray(patch[key]) || !patch[key].every(item => typeof item === 'string'))) {\n return false\n }\n }\n return Object.keys(patch).length > 0\n}\n\n/**\n * upsert-automation 输入的严格校验(§11.2:exact keys 仅 id/enabled/source/\n * trigger/config/filter;trigger 仅 kind/cron;filter 仅 labels)。\n * cron 语法合法、source 枚举、config 全部字符串值;nextRunAt/lastTriggeredAt\n * 为 Host 独占字段,不在输入面内。\n */\nfunction automationRuleInput(value: unknown): value is AutomationRuleInput {\n const rule = record(value)\n if (rule === undefined || !exactKeys(rule, ['id', 'enabled', 'source', 'trigger', 'config', 'filter'])) {\n return false\n }\n if (typeof rule.id !== 'string' || rule.id.trim() === '' || rule.id.length > 128) return false\n if (typeof rule.enabled !== 'boolean') return false\n if (rule.source === undefined || !isAutomationSource(rule.source)) return false\n const trigger = record(rule.trigger)\n if (trigger === undefined || !exactKeys(trigger, ['kind', 'cron'])) return false\n if (trigger.kind !== 'cron' || typeof trigger.cron !== 'string' || !isValidCron(trigger.cron)) return false\n const config = record(rule.config)\n if (config === undefined || !Object.values(config).every(value => typeof value === 'string')) return false\n const filter = record(rule.filter)\n if (filter === undefined || !exactKeys(filter, ['labels'])) return false\n if (!Array.isArray(filter.labels) || !filter.labels.every(label => typeof label === 'string')) return false\n return true\n}\n\n/**\n * 解析幂等 action 信封(§11.2/§11.3)。任何结构/类型/枚举/时间戳/cron/未知\n * 字段/未知 action 问题 → undefined(路由层 400 invalid-action)。返回的 action\n * 为规范化对象(import 任务行经严格校验与字段剥离)。\n *\n * T009(§11.2/D10):\n * - `reorder`:`order` 必填有限数字(浏览器提交的落位下标,0-based;Host 重算\n * 同列唯一,不信任浏览器序号——见 transitions.applyReorderTask);`status` 须\n * 为合法状态且与任务当前状态一致(防竞态,状态机层校验);`project` 可选字符串\n * (空串 = 清除归属,沿用 update 语义);`tags` 可选字符串数组(整体替换,\n * 沿用 update 的 normalizeTags 语义)。\n * - `move`:新增可选 `order`(有限数字 = 目标列落位下标,缺省追加目标列末尾)。\n */\nexport function parseActionEnvelope(value: unknown): TaskBoardActionEnvelope | undefined {\n const envelope = record(value)\n if (envelope === undefined || !exactKeys(envelope, ['requestId', 'action'])) return undefined\n if (typeof envelope.requestId !== 'string' || envelope.requestId.trim() === '' || envelope.requestId.length > 256) {\n return undefined\n }\n const action = record(envelope.action)\n if (action === undefined || typeof action.kind !== 'string') return undefined\n const taskId = typeof action.taskId === 'string' && action.taskId.trim() !== '' ? action.taskId : undefined\n switch (action.kind) {\n case 'import':\n if (!exactKeys(action, ['kind', 'sourceId', 'tasks'])) return undefined\n if (typeof action.sourceId !== 'string' || action.sourceId === '' || !Array.isArray(action.tasks)) return undefined\n {\n const tasks: TaskRecord[] = []\n for (const row of action.tasks) {\n const task = strictImportedTask(row)\n if (task === undefined) return undefined\n tasks.push(task)\n }\n return { requestId: envelope.requestId, action: { kind: 'import', sourceId: action.sourceId, tasks } }\n }\n case 'create':\n if (!exactKeys(action, ['kind', 'id', 'input'])) return undefined\n return typeof action.id === 'string' && action.id.trim() !== '' && createInput(action.input)\n ? { requestId: envelope.requestId, action: { kind: 'create', id: action.id, input: action.input } }\n : undefined\n case 'propose':\n // T011:对话提取/手动转任务 → 创建 proposed 候选。输入沿用 NewTaskInput\n // 校验 + sourceConversationId/sourceMessageId 可选字符串;命令类字段\n // 递归拒绝(§16.7)。\n if (!exactKeys(action, ['kind', 'id', 'input'])) return undefined\n return typeof action.id === 'string' && action.id.trim() !== '' && proposeInput(action.input)\n ? { requestId: envelope.requestId, action: { kind: 'propose', id: action.id, input: action.input } }\n : undefined\n case 'propose-batch':\n // T012:批量创建候选任务(需求拆分产物,§14.5)。items 逐条沿用 propose\n // 校验 + parentId 可选字符串;空 items / 任一条目非法 → 整包拒绝(协议层\n // 拒绝即 invalid-action,原子性由状态机落账层保证——见 applyProposeBatch)。\n if (!exactKeys(action, ['kind', 'id', 'items'])) return undefined\n if (typeof action.id !== 'string' || action.id.trim() === '' || !Array.isArray(action.items)) return undefined\n if (action.items.length === 0) return undefined\n {\n const items: BatchProposeItem[] = []\n for (const row of action.items) {\n if (!batchProposeItem(row)) return undefined\n items.push(row)\n }\n return { requestId: envelope.requestId, action: { kind: 'propose-batch', id: action.id, items } }\n }\n case 'start-split':\n // T012:需求拆分驱动(§12.9/§14.5)。输入严格校验(标题非空、text/filePath\n // 至少其一);Host 启动独立拆分会话,结算时经父任务 + propose-batch 落账。\n if (!exactKeys(action, ['kind', 'id', 'input'])) return undefined\n return typeof action.id === 'string' && action.id.trim() !== '' && splitRequestInput(action.input)\n ? { requestId: envelope.requestId, action: { kind: 'start-split', id: action.id, input: action.input } }\n : undefined\n case 'confirm':\n // T011:确认候选 → 落列。目标仅 backlog | todo(§10.2)。\n if (!exactKeys(action, ['kind', 'taskId', 'target'])) return undefined\n return taskId !== undefined && (action.target === 'backlog' || action.target === 'todo')\n ? { requestId: envelope.requestId, action: { kind: 'confirm', taskId, target: action.target } }\n : undefined\n case 'dismiss':\n // T011:拒绝/忽略候选(删除)。\n if (!exactKeys(action, ['kind', 'taskId'])) return undefined\n return taskId === undefined\n ? undefined\n : { requestId: envelope.requestId, action: { kind: 'dismiss', taskId } }\n case 'update':\n if (!exactKeys(action, ['kind', 'taskId', 'patch'])) return undefined\n return taskId !== undefined && updatePatch(action.patch)\n ? { requestId: envelope.requestId, action: { kind: 'update', taskId, patch: action.patch } }\n : undefined\n case 'set-schedule':\n if (!exactKeys(action, ['kind', 'taskId', 'patch'])) return undefined\n return taskId !== undefined && schedulePatch(action.patch)\n ? { requestId: envelope.requestId, action: { kind: 'set-schedule', taskId, patch: action.patch } }\n : undefined\n case 'set-one-shot':\n if (!exactKeys(action, ['kind', 'taskId', 'patch'])) return undefined\n return taskId !== undefined && oneShotPatch(action.patch)\n ? { requestId: envelope.requestId, action: { kind: 'set-one-shot', taskId, patch: action.patch } }\n : undefined\n case 'move':\n // T009:可选 order(有限数字 = 目标列落位下标,缺省追加末尾)。\n if (!exactKeys(action, ['kind', 'taskId', 'status', 'order'])) return undefined\n if (taskId === undefined || !isTaskStatus(action.status)) return undefined\n if (action.order !== undefined && !isFiniteNumber(action.order)) return undefined\n return action.order === undefined\n ? { requestId: envelope.requestId, action: { kind: 'move', taskId, status: action.status } }\n : { requestId: envelope.requestId, action: { kind: 'move', taskId, status: action.status, order: action.order } }\n case 'reorder':\n // T009:order 必填有限数字;project 可选字符串(空串清除);tags 可选字符串数组。\n if (!exactKeys(action, ['kind', 'taskId', 'status', 'order', 'project', 'tags'])) return undefined\n if (taskId === undefined || !isTaskStatus(action.status) || !isFiniteNumber(action.order)) return undefined\n if (action.project !== undefined && typeof action.project !== 'string') return undefined\n if (action.tags !== undefined\n && (!Array.isArray(action.tags) || !action.tags.every(tag => typeof tag === 'string'))) {\n return undefined\n }\n return {\n requestId: envelope.requestId,\n action: {\n kind: 'reorder',\n taskId,\n status: action.status,\n order: action.order,\n ...(typeof action.project === 'string' ? { project: action.project } : {}),\n ...(Array.isArray(action.tags) ? { tags: action.tags as string[] } : {}),\n },\n }\n case 'delete':\n case 'archive':\n case 'restore':\n case 'run':\n case 'rerun':\n if (!exactKeys(action, ['kind', 'taskId'])) return undefined\n return taskId === undefined\n ? undefined\n : { requestId: envelope.requestId, action: { kind: action.kind, taskId } }\n case 'add-comment':\n if (!exactKeys(action, ['kind', 'taskId', 'comment'])) return undefined\n return taskId !== undefined && commentInput(action.comment)\n ? { requestId: envelope.requestId, action: { kind: 'add-comment', taskId, comment: action.comment } }\n : undefined\n case 'add-artifact':\n if (!exactKeys(action, ['kind', 'taskId', 'artifact'])) return undefined\n return taskId !== undefined && artifactInput(action.artifact)\n ? { requestId: envelope.requestId, action: { kind: 'add-artifact', taskId, artifact: action.artifact } }\n : undefined\n case 'update-context':\n if (!exactKeys(action, ['kind', 'taskId', 'patch'])) return undefined\n return taskId !== undefined && contextPatch(action.patch)\n ? { requestId: envelope.requestId, action: { kind: 'update-context', taskId, patch: action.patch } }\n : undefined\n case 'upsert-automation':\n if (!exactKeys(action, ['kind', 'rule'])) return undefined\n return automationRuleInput(action.rule)\n ? { requestId: envelope.requestId, action: { kind: 'upsert-automation', rule: action.rule } }\n : undefined\n case 'delete-automation':\n case 'run-automation':\n if (!exactKeys(action, ['kind', 'ruleId'])) return undefined\n return typeof action.ruleId === 'string' && action.ruleId.trim() !== ''\n ? { requestId: envelope.requestId, action: { kind: action.kind, ruleId: action.ruleId } }\n : undefined\n default:\n return undefined\n }\n}\n","/**\n * 浏览器 ↔ Host 的同源传输层(T004 §11.1/§11.3/§12 实时同步)。\n *\n * 三个端点(与 T003 host-routes 一一对应):\n * - `GET /api/nova-task-board/state` 全量快照(含 tasks),`no-store` 防缓存;\n * - `POST /api/nova-task-board/action` 幂等信封 `{requestId, action}`,返回新快照;\n * - `GET /api/nova-task-board/events` SSE 只推 `{revision, scheduler, power}`\n * (15s 心跳),revision 变化才由控制器重拉 state。\n *\n * 同步契约(§17 一致性):浏览器不先写未确认状态——本层只是传输,动作是否生效\n * 以 Host 返回的快照为准;SSE 连接中断时浏览器 EventSource 自动重连,重连成功\n * (onopen)与页面重新可见(visibilitychange → visible)都会触发一次全量重拉。\n *\n * v1 迁移(§11.2 import / §9.5):首次加载若本地仍有 v1 回退数据且尚未导入\n * 本项目账本(按 ledgerId 记 marker),则用一次性 sourceId/requestId 提交\n * `import` action;marker 只在 Host 确认(返回快照)后写入,原 v1 值永不删除。\n * 迁移键用本项目专属前缀 `dsh.novaTaskBoard.*`,避免与参考实现自己的\n * `dsh.taskBoard.v2.*` 迁移标记互踩(AGENTS.md D2/D3)。\n */\n\nimport type { TaskRecord } from '../core/model.ts'\nimport {\n TASK_BOARD_API_PREFIX,\n type TaskBoardAction,\n type TaskBoardActionEnvelope,\n type TaskBoardEventPayload,\n type TaskBoardSnapshot,\n} from '../protocol.ts'\n/** 单次 Host 请求超时(§17 性能边界;超时按传输错误暴露,可重试)。 */\nexport const REQUEST_TIMEOUT_MS = 15_000\n\n/** v1 迁移标记:本项目账本的 ledgerId(Host 确认后写入,防止重复导入)。 */\nconst IMPORT_MARKER = 'dsh.novaTaskBoard.v2.hostImported'\n/** v1 迁移 sourceId(一次迁移一个,写入后持久)。 */\nconst SOURCE_KEY = 'dsh.novaTaskBoard.v2.sourceId'\n/** v1 迁移 requestId(跨 Host 重启重试保持幂等)。 */\nconst IMPORT_REQUEST_KEY = 'dsh.novaTaskBoard.v2.importRequestId'\n\n/** 浏览器存储的读写面(localStorage;测试注入 fake)。 */\nexport interface MarkerStorage {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n}\n\n/** 传输层对外契约:控制器只依赖这四个方法(结构面,测试可注入 fake)。 */\nexport interface TaskBoardHostTransport {\n /** 首次同步:先拉全量 state,必要时顺带做一次 v1 一次性迁移(§11.2 import)。 */\n bootstrap(legacy: readonly TaskRecord[]): Promise<TaskBoardSnapshot>\n /** 全量快照。 */\n state(): Promise<TaskBoardSnapshot>\n /** 提交幂等动作,返回 Host 确认后的新快照(不先写未确认状态)。 */\n action(action: TaskBoardAction): Promise<TaskBoardSnapshot>\n /**\n * 订阅实时事件。listener 带载荷 = SSE 帧(revision/scheduler/power);不带\n * 载荷 = 同步信号(重连成功/页面重新可见),控制器据此重拉全量。\n */\n subscribe(listener: (event?: TaskBoardEventPayload) => void): () => void\n}\n\n/** 浏览器环境可用的 uuid(无 crypto 时退化为时间戳 + 随机串)。 */\nfunction uuid(): string {\n return globalThis.crypto?.randomUUID?.()\n ?? `browser-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n}\n\n/** 读 JSON 响应体;非 2xx 按 Host 的错误信息抛错(§11.1 400/403/413/415)。 */\nasync function readJson<T>(response: Response): Promise<T> {\n const body = await response.json() as T & { error?: string }\n if (!response.ok) throw new Error(body.error ?? `task-board request failed: ${response.status}`)\n return body\n}\n\n/**\n * 真实 HTTP + SSE 传输(同源 `/api/nova-task-board/*`)。\n * @param markers - 迁移标记存储(缺省用全局 localStorage)。\n */\nexport class HttpTaskBoardHostTransport implements TaskBoardHostTransport {\n constructor(private readonly markers: MarkerStorage | undefined = globalThis.localStorage) {}\n\n async bootstrap(legacy: readonly TaskRecord[]): Promise<TaskBoardSnapshot> {\n const initial = await this.state()\n const ledgerId = initial.scheduler.ledgerId\n if (legacy.length > 0 && ledgerId !== undefined && this.markers?.getItem(IMPORT_MARKER) !== ledgerId) {\n // 旧数据尚未导入本项目账本:一次性迁移。sourceId/requestId 持久化,Host\n // 重启后重试同一 requestId 仍幂等(§11.3)。\n let sourceId = this.markers?.getItem(SOURCE_KEY)\n if (sourceId === null || sourceId === undefined || sourceId === '') {\n sourceId = uuid()\n this.markers?.setItem(SOURCE_KEY, sourceId)\n }\n let requestId = this.markers?.getItem(IMPORT_REQUEST_KEY)\n if (requestId === null || requestId === undefined || requestId === '') {\n requestId = uuid()\n this.markers?.setItem(IMPORT_REQUEST_KEY, requestId)\n }\n const snapshot = await this.post(requestId, { kind: 'import', sourceId, tasks: [...legacy] })\n // marker 只在 Host 确认(原子落账返回快照)后写入。\n this.markers?.setItem(IMPORT_MARKER, snapshot.scheduler.ledgerId ?? ledgerId)\n return snapshot\n }\n return initial\n }\n\n async state(): Promise<TaskBoardSnapshot> {\n return await this.request(`${TASK_BOARD_API_PREFIX}/state`, { cache: 'no-store' })\n }\n\n async action(action: TaskBoardAction): Promise<TaskBoardSnapshot> {\n return await this.post(uuid(), action)\n }\n\n private async post(requestId: string, action: TaskBoardAction): Promise<TaskBoardSnapshot> {\n const envelope: TaskBoardActionEnvelope = { requestId, action }\n return await this.request(`${TASK_BOARD_API_PREFIX}/action`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(envelope),\n })\n }\n\n /** 同源 fetch,带超时(超时按传输错误抛,控制器转为可重试的错误条)。 */\n private async request(url: string, init: RequestInit): Promise<TaskBoardSnapshot> {\n const controller = new AbortController()\n const timeout = globalThis.setTimeout(() => { controller.abort() }, REQUEST_TIMEOUT_MS)\n try {\n return await readJson<TaskBoardSnapshot>(await fetch(url, { ...init, signal: controller.signal }))\n } catch (error) {\n if (controller.signal.aborted) {\n throw new Error(`task-board Host request timed out after ${REQUEST_TIMEOUT_MS / 1_000}s`)\n }\n throw error\n } finally {\n globalThis.clearTimeout(timeout)\n }\n }\n\n /**\n * SSE 订阅:消息帧解析为 `{revision, scheduler, power}`(解析失败按同步信号\n * 处理,重拉兜底);`onopen`(含断线重连成功)与页面重新可见 → 同步信号,\n * 控制器据此重拉全量 state。\n */\n subscribe(listener: (event?: TaskBoardEventPayload) => void): () => void {\n const events = new EventSource(`${TASK_BOARD_API_PREFIX}/events`)\n events.onmessage = (message: MessageEvent<string>): void => {\n try {\n const parsed = JSON.parse(message.data) as TaskBoardEventPayload\n if (parsed === null || typeof parsed !== 'object' || typeof parsed.revision !== 'number') {\n throw new Error('invalid event frame')\n }\n listener(parsed)\n } catch {\n listener()\n }\n }\n events.onopen = (): void => { listener() }\n const onVisible = (): void => {\n if (document.visibilityState === 'visible') listener()\n }\n document.addEventListener('visibilitychange', onVisible)\n return () => {\n document.removeEventListener('visibilitychange', onVisible)\n events.close()\n }\n }\n}\n","/**\n * v1 只读回退数据读取(T004 §12 范围项 8:UI 层对 v1 只读回退数据可正常展示)。\n *\n * 参考实现的旧浏览器账本写在 localStorage `dsh.taskBoard.v1`(键名由其\n * `DEFAULT_STORAGE_KEY` 定义,与宿主/参考实现共享同一浏览器源)。本项目与其\n * 共存(AGENTS.md D3):读取该键做一次性迁移,但**绝不删除原值**——迁移后它\n * 仍是只读回退副本;迁移经 Host 的 `import` action 落进本项目自己的账本\n * (`$DSH_HOME/nova-ui/task-board/`,AGENTS.md D2),写入方只有 Host。\n *\n * 行解析走本项目 core/model 的 `normalizeTask`(宽松归一化:未知字段剥离、\n * 状态回退 todo、坏 schedule 修复或丢弃、不丢行),单条坏行被丢弃而不阻断\n * 整批——Host 侧 `import` 是严格逐行校验,任何坏行都会让整批 import 被拒,\n * 所以浏览器侧先做一次宽松过滤,保证一条陈旧坏数据不阻断迁移。\n */\n\nimport type { TaskRecord } from '../core/model.ts'\nimport { normalizeTask } from '../core/model.ts'\n\n/** 参考实现的 v1 浏览器账本键(只读来源;其内部键名 `dsh.taskBoard.v1`)。 */\nexport const LEGACY_V1_STORAGE_KEY = 'dsh.taskBoard.v1'\n\n/** 浏览器 localStorage 的读取面(测试注入 fake)。 */\nexport interface LegacyStorage {\n getItem(key: string): string | null\n}\n\n/**\n * 读取并宽松归一化 v1 回退数据。存储缺失/损坏/非数组 → [](降级为空账本,\n * 绝不抛错拖垮看板启动);数组内逐行 normalizeTask,坏行丢弃。\n * @param storage - 存储面(缺省用全局 localStorage;非浏览器环境为 undefined → [])。\n * @param key - 覆盖存储键(测试用)。\n */\nexport function readLegacyV1Tasks(\n storage: LegacyStorage | undefined = globalThis.localStorage,\n key: string = LEGACY_V1_STORAGE_KEY,\n): TaskRecord[] {\n if (storage === undefined) return []\n let raw: string | null\n try {\n raw = storage.getItem(key)\n } catch (error) {\n console.error('[dsh-task-board] v1 ledger read failed; skipping migration', error)\n return []\n }\n if (raw === null) return []\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n console.error('[dsh-task-board] v1 ledger is not valid JSON; skipping migration', error)\n return []\n }\n if (!Array.isArray(parsed)) {\n console.error('[dsh-task-board] v1 ledger is not an array; skipping migration')\n return []\n }\n const tasks: TaskRecord[] = []\n for (const row of parsed) {\n const task = normalizeTask(row)\n if (task === undefined) {\n const id = typeof row === 'object' && row !== null ? (row as { id?: unknown }).id : undefined\n console.warn('[dsh-task-board] dropping invalid v1 task row', typeof id === 'string' ? id : row)\n continue\n }\n tasks.push(task)\n }\n return tasks\n}\n","/**\n * Nova sidebar entry injection core (adapted from the reference dsh-web-ui\n * shared/client/sidebar-entry-core.ts; mechanism identical, attributes\n * namespaced to data-dsh-nova-* for coexistence, AGENTS.md D3).\n *\n * dsh's sidebar shell exposes no slot an external plugin can register into,\n * so the entry row is injected between the shell's New Session button and the\n * workspace browser. The injection self-heals: a MutationObserver watches the\n * sidebar root and re-inserts the row whenever a React re-render displaces it\n * (re-insertion happens in the same frame, before paint, so no flicker).\n *\n * The row is plain DOM (no React tree) so it can never disturb the shell's\n * reconciliation; the view it toggles is a separate root owned by the caller.\n */\n\n/** Per-package configuration for one sidebar entry row. */\nexport interface NovaSidebarEntryOptions {\n /** Full attribute name identifying the injected row (idempotency key). */\n rowAttribute: string\n /** CSS selector matching the injected row. */\n rowSelector: string\n /** L2 semantic-attribute plugin id (data-dsh-plugin value). */\n plugin?: string\n /** Inline icon markup (matches the shell's 16px nav-icon look). */\n icon: string\n /** CSS module class names for the row and its two spans (entry / entryIcon / entryLabel). */\n css: Record<string, string>\n /** Localized row label (aria-label + visible text). */\n label(): string\n /** Optional localized tooltip (title attribute). */\n tooltip?(): string\n /** Click action (open/toggle the owning panel). */\n onToggle(): void\n /** Family-block position: 'before' inserts ahead of sibling plugin rows, 'after' behind them. */\n position: 'before' | 'after'\n /** Selectors of the sibling plugin entry rows this package orders against (its own row included). */\n familySelectors: readonly string[]\n /** Optional active-state bridge; highlights the row while the panel is open. */\n active?: {\n subscribe(listener: () => void): () => void\n isOpen(): boolean\n }\n /** Optional numeric badge (T011 §12.7 未确认候选数角标); hidden at 0. */\n badge?: {\n subscribe(listener: () => void): () => void\n count(): number\n }\n}\n\n/** Find the sidebar shell root element, or undefined while not yet mounted. */\nfunction sidebarRoot(): HTMLElement | undefined {\n const column = document.querySelector<HTMLElement>('[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]')\n if (column === null) return undefined\n const logoOwner = column.querySelector<HTMLElement>('[class*=\"logoRow\"]')?.parentElement\n return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)\n}\n\n/** The New Session button: nested in the logo row on current shells, a direct child on legacy shells. */\nfunction newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {\n const nested = root.querySelector<HTMLButtonElement>('button[class*=\"newSession\"]')\n if (nested !== null) return nested\n for (const child of root.children) {\n if (child.tagName === 'BUTTON') return child as HTMLButtonElement\n }\n return undefined\n}\n\n/** Build the entry row (a detached button; insert once the shell is up). */\nfunction createEntry(options: NovaSidebarEntryOptions): HTMLButtonElement {\n const entry = document.createElement('button')\n entry.type = 'button'\n entry.setAttribute(options.rowAttribute, '')\n if (options.plugin !== undefined) {\n entry.setAttribute('data-dsh-plugin', options.plugin)\n entry.setAttribute('data-dsh-part', 'sidebar-entry')\n }\n entry.className = options.css['entry'] ?? ''\n entry.setAttribute('aria-label', options.label())\n if (options.tooltip !== undefined) entry.setAttribute('title', options.tooltip())\n entry.innerHTML = '<span class=\"' + (options.css['entryIcon'] ?? '') + '\">' + options.icon\n + '</span><span class=\"' + (options.css['entryLabel'] ?? '') + '\">' + options.label() + '</span>'\n if (options.badge !== undefined) {\n const badge = document.createElement('span')\n badge.className = options.css['entryBadge'] ?? ''\n badge.dataset.dshPart = 'entry-badge'\n badge.setAttribute('aria-hidden', 'true')\n entry.appendChild(badge)\n }\n entry.addEventListener('click', options.onToggle)\n return entry\n}\n\n/** Re-insert the entry after the New Session row (before the browser region). */\nfunction placeEntry(root: HTMLElement, entry: HTMLButtonElement, options: NovaSidebarEntryOptions): boolean {\n const button = newSessionButton(root)\n if (button === undefined) return false\n if (entry.parentElement !== root) {\n const row = button.closest('[class*=\"logoRow\"]')\n const base = (row !== null && row.parentElement === root) ? row : button\n const family = Array.from(root.children).filter(\n (el): el is HTMLElement => el instanceof HTMLElement && el.matches(options.familySelectors.join(', ')),\n )\n const anchor = options.position === 'before'\n ? (family.length > 0 ? family[0] : base.nextElementSibling)\n : (family.length > 0 ? family[family.length - 1]!.nextElementSibling : base.nextElementSibling)\n root.insertBefore(entry, anchor)\n }\n return true\n}\n\n/**\n * Mount the sidebar entry, waiting for the shell to render and self-healing\n * on later React re-renders.\n * @param options - the row's attribute/icon/copy/action/ordering configuration.\n * @returns disposer removing the entry and its observers.\n */\nexport function mountSidebarEntry(options: NovaSidebarEntryOptions): () => void {\n if (typeof document !== 'undefined' && document.querySelector(options.rowSelector) !== null) {\n return () => {}\n }\n const entry = createEntry(options)\n let root: HTMLElement | undefined\n let placed = false\n\n const tryPlace = (): void => {\n if (root !== undefined && !root.isConnected) {\n rootObserver.disconnect()\n root = undefined\n placed = false\n }\n if (placed) {\n if (document.body.contains(entry)) return\n rootObserver.disconnect()\n root = undefined\n placed = false\n }\n root ??= sidebarRoot()\n if (root === undefined) return\n placed = placeEntry(root, entry, options)\n if (placed) {\n rootObserver.observe(root, { childList: true, subtree: true })\n }\n }\n\n const waitObserver = new MutationObserver(() => { tryPlace() })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const rootObserver = new MutationObserver(() => {\n if (root === undefined || !root.isConnected) {\n placed = false\n tryPlace()\n return\n }\n if (!root.contains(entry)) {\n placed = placeEntry(root, entry, options)\n }\n })\n\n const unsubscribeActive = options.active === undefined ? undefined : (() => {\n const syncActive = (): void => {\n if (options.active!.isOpen()) entry.dataset.active = 'true'\n else delete entry.dataset.active\n }\n const unsubscribe = options.active.subscribe(syncActive)\n syncActive()\n return unsubscribe\n })()\n\n const unsubscribeBadge = options.badge === undefined ? undefined : (() => {\n const badge = entry.querySelector<HTMLElement>('[data-dsh-part=\"entry-badge\"]')\n if (badge === null) return undefined\n const syncBadge = (): void => {\n const count = options.badge!.count()\n badge.textContent = String(count)\n badge.dataset.count = String(count)\n badge.title = count === 0 ? '' : `${options.label()} · ${count}`\n }\n const unsubscribe = options.badge.subscribe(syncBadge)\n syncBadge()\n return unsubscribe\n })()\n\n tryPlace()\n\n return () => {\n waitObserver.disconnect()\n rootObserver.disconnect()\n unsubscribeActive?.()\n unsubscribeBadge?.()\n entry.remove()\n }\n}\n","/**\n * Sidebar entry injection — package-specific wiring over the shared core.\n *\n * The entry row is injected between the shell's New Session button and the\n * workspace browser (data-dsh-nova-taskboard-entry, nova- 前缀与参考实现\n * data-dsh-taskboard-entry 区分,见 AGENTS.md D3)。The row is plain DOM (no\n * React tree); the board view it toggles is a separate React root mounted in\n * the center column (see board-mount.tsx).\n */\nimport type { BoardController } from './board-controller.ts'\nimport { proposedCountOf } from './board-controller.ts'\nimport { t } from './locales.ts'\nimport css from './board.module.css'\nimport { mountSidebarEntry as mountSidebarEntryCore } from './sidebar-entry-core.ts'\n\n/** Stable data attribute identifying the injected entry row. */\nexport const ENTRY_SELECTOR = '[data-dsh-nova-taskboard-entry]'\n\n/** Inline icon (matches the shell's 16px nav-icon look). */\nconst ICON = '<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2.5\" width=\"12\" height=\"11\" rx=\"1.5\"/><path d=\"M2 6.5h12M6.5 6.5v7\"/></svg>'\n\n/**\n * Mount the sidebar entry, waiting for the shell to render and self-healing\n * on later React re-renders.\n * @param controller - the board controller the entry toggles.\n * @returns disposer removing the entry and its observers.\n */\nexport function mountSidebarEntry(controller: BoardController): () => void {\n return mountSidebarEntryCore({\n rowAttribute: 'data-dsh-nova-taskboard-entry',\n rowSelector: ENTRY_SELECTOR,\n plugin: 'nova-task-board',\n icon: ICON,\n css,\n label: () => t('entry.label'),\n onToggle: () => { controller.toggleBoard() },\n position: 'before',\n familySelectors: ['[data-dsh-nova-taskboard-entry]'],\n active: {\n subscribe: (listener) => controller.subscribe(listener),\n isOpen: () => controller.getSnapshot().boardOpen,\n },\n // T011 §12.7 通知:新增候选任务时入口显示未确认数量角标(proposed 且未归档)。\n badge: {\n subscribe: (listener) => controller.subscribe(listener),\n count: () => proposedCountOf(controller.getSnapshot()),\n },\n })\n}\n","/**\n * Nova task-board settings card skeleton (T001).\n *\n * Binds the `nova-task-board` settings namespace and renders a compact staged\n * form (enabled / announceToAgent / preventIdleSleep). 完整设置卡片形态\n * (电源状态、边界说明等)由后续任务扩展;本骨架先打通「可见 + 可切换插件\n * 开关」的验收路径(需求 12.5)。\n */\nimport { useEffect, useState } from 'react'\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport { t } from './locales.ts'\n\n/** The task-board fields this card edits (the namespace's full schema). */\nexport interface NovaTaskBoardSettings {\n /** Master switch for the plugin. */\n enabled?: boolean\n /** Whether the board announces itself in every agent's system prompt. */\n announceToAgent?: boolean\n /** Prevent host idle sleep while sessions run or schedules are armed. */\n preventIdleSleep?: boolean\n}\n\n/** The boolean fields the card renders, in order. */\nconst BOOLEAN_FIELDS: ReadonlyArray<{\n field: keyof NovaTaskBoardSettings\n label: string\n hint: string\n}> = [\n { field: 'enabled', label: t('settings.enabled'), hint: t('settings.enabledHint') },\n { field: 'announceToAgent', label: t('settings.announceToAgent'), hint: t('settings.announceToAgentHint') },\n]\n\n/**\n * The settings card component, bound to the `nova-task-board` scope.\n * @param props - the bound scope for the card's namespace.\n */\nexport function NovaTaskBoardSettingsCard(props: { scope: SettingsScope<NovaTaskBoardSettings> }): JSX.Element | null {\n const { scope } = props\n const [snapshot, setSnapshot] = useState<SettingsScopeSnapshot<NovaTaskBoardSettings>>(() => scope.getSnapshot())\n\n useEffect(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope])\n\n if (snapshot.status === 'loading') return null\n if (snapshot.status !== 'ready') {\n return (\n <div>\n <p>{t('settings.description')}</p>\n <p>{t('settings.notExposed')}</p>\n </div>\n )\n }\n if (!snapshot.writable) {\n return (\n <div>\n <p>{t('settings.description')}</p>\n <p>{t('settings.readOnly')}</p>\n </div>\n )\n }\n\n const value = snapshot.value ?? {}\n\n const toggle = (field: keyof NovaTaskBoardSettings): void => {\n const current = value[field] === true\n if (current) void scope.unset(field as string)\n else void scope.set(field as string, true)\n }\n\n return (\n <div>\n <p>{t('settings.description')}</p>\n {BOOLEAN_FIELDS.map(({ field, label, hint }) => (\n <label key={field} style={{ display: 'block', margin: '8px 0' }}>\n <input\n type=\"checkbox\"\n checked={value[field] === true}\n onChange={() => toggle(field)}\n />\n {' '}{label}\n <div style={{ fontSize: 12, opacity: 0.7 }}>{hint}</div>\n </label>\n ))}\n </div>\n )\n}\n","/**\n * Nova task-board client plugin (T004): wires the framework-free controller\n * to the real client runtime and mounts the two DOM surfaces — the sidebar\n * entry row and the board view in the center column — plus the settings card\n * over the `nova-task-board` namespace.\n *\n * 接线(对齐参考实现的运行时面,但全部标识用 nova- 前缀,AGENTS.md D3):\n * - transport:同源 HTTP + SSE(host-api.ts,`/api/nova-task-board/*`),\n * v1 回退数据(localStorage `dsh.taskBoard.v1`)经其一次性迁移;\n * - sessions:会话列表订阅(看板打开期间 current 变化自动关闭看板)+ 详情页\n * 「查看会话」跳转 transcript;\n * - workspaces / connection.api.agentPresets:执行目标三件套下拉的选项源\n * (工作区列表 + agent 预设名册,运行时事实而非账本)。\n *\n * Failure policy: DOM mounting problems are logged, never thrown — the web\n * shell fails the whole boot when a plugin apply throws, and an external\n * plugin must not take the GUI down.\n */\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'\nimport type { ISessions, IWorkspaces, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale) and its\n// LocaleNamespaceMap merge table.\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { BoardController } from './board-controller.ts'\nimport { claimNovaTaskBoardApply, releaseNovaTaskBoardApply } from './apply-guard.ts'\nimport { mountBoard } from './board-mount.tsx'\nimport { mountChatToTaskBoard, mountMessageConvertButton, type ChatSessionsFace } from './chat-integration.tsx'\nimport { HttpTaskBoardHostTransport } from './host-api.ts'\nimport { readLegacyV1Tasks } from './legacy-store.ts'\nimport { mountSidebarEntry } from './sidebar-entry.ts'\nimport { NovaTaskBoardSettingsCard, type NovaTaskBoardSettings } from './NovaTaskBoardSettingsCard.tsx'\nimport { en, zh, type NovaTaskBoardKey } from './locales.ts'\n\n/** Locale namespace this plugin owns. */\nconst NS = 'nova-task-board'\n\n/** Settings namespace the settings card edits (the Host plugin registers it). */\nconst TASK_BOARD_NS = 'nova-task-board'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Nova task-board surface copy. */\n 'nova-task-board': NovaTaskBoardKey\n }\n\n interface SlotMap {\n /**\n * The child slot the Web UI plugin group declares; this card registers\n * into the group instead of the top-level `settings.section` list.\n * Spelled here with the same shape so this package can register without\n * depending on the sibling UI package.\n */\n 'web-ui.plugin.item': { kind: 'list'; scope: 'root'; owner: WebUiPluginItemOwnerProps }\n }\n}\n\n/** Owner share of a plugin card (the section supplies nothing). */\nexport interface WebUiPluginItemOwnerProps {\n /** Marker field: card owner props are intentionally empty. */\n children?: never\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-ui-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/** Required services (fiber inject waiting — the runtime must be up first). */\nexport const inject = ['slots', 'sessions', 'workspaces', 'connection', 'settingsScope', 'locale', 'remote'] as const\n\n/**\n * Mount the nova task board (T004 真实看板).\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n // A duplicated client injection (module factory executed twice in one page\n // lifetime) would otherwise mount a second sidebar entry and board view.\n if (!claimNovaTaskBoardApply()) return\n\n ctx.effect(() => releaseNovaTaskBoardApply, 'nova-task-board: apply claim')\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'nova-task-board: dictionaries')\n\n // Plugin configuration card: one staged form over the `nova-task-board`\n // settings namespace, contributed to the Web UI plugin group.\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<NovaTaskBoardSettings>({ namespace: TASK_BOARD_NS })\n ctx.slots.inject('web-ui.plugin.item', () => {\n const unregister = ctx.slots.register({\n name: 'web-ui.plugin.item',\n id: 'nova-task-board',\n order: 110,\n locale: NS,\n inject: () => ({ scope: settingsScope }),\n }, NovaTaskBoardSettingsCard)\n return () => {\n unregister()\n }\n })\n\n // The sidebar entry and board view mount once the settings scope settles;\n // while the scope is still loading, the composition default is unknown, so\n // nothing mounts yet. Only an unavailable scope (no settings surface served)\n // falls back to the composition default (enabled).\n let uiDisposer: (() => void) | undefined\n const mountUi = (): void => {\n if (uiDisposer !== undefined) return\n\n // Host and browser SDK declarations share the Cordis Context name. Read\n // the browser faces explicitly so Host-side declaration merging cannot\n // narrow these client services during a combined package build.\n const sessions = ctx.get('sessions') as unknown as ISessions\n const workspaces = ctx.get('workspaces') as unknown as IWorkspaces\n const connection = ctx.get('connection') as unknown as ConnectionHandle\n\n const controller = new BoardController({\n transport: new HttpTaskBoardHostTransport(),\n sessions: {\n list: sessions.list,\n open: id => sessions.open(id as SessionId),\n },\n legacy: () => readLegacyV1Tasks(),\n })\n controller.start()\n\n const disposers: Array<() => void> = []\n\n // Execution-target option feeds: the workspace list drives the workspace\n // picker, and the agent-preset roster drives the mode picker. Both are\n // runtime facts (not ledger state), so the wiring pushes them into the\n // controller on change; the preset roster is re-read after reconnects\n // because a reconnect may serve a different deployment.\n const pushWorkspaceOptions = (): void => {\n const snapshot = workspaces.list.getSnapshot()\n controller.setExecutionOptions({\n workspaces: snapshot.items.map(item => ({\n workspaceId: item.workspaceId,\n title: item.title !== '' ? item.title : item.path,\n })),\n })\n }\n pushWorkspaceOptions()\n disposers.push(workspaces.list.subscribe(pushWorkspaceOptions))\n const pushPresetOptions = async (): Promise<void> => {\n try {\n const response = await connection.api.agentPresets.list({})\n if (!response.result.ok) return\n controller.setExecutionOptions({\n presets: response.result.value.presets.map(preset => ({\n id: preset.id,\n name: preset.name,\n description: preset.description,\n broken: preset.broken,\n isDefault: preset.isDefault,\n })),\n })\n } catch (error) {\n // A failed roster read leaves the previous options in place; the\n // picker stays usable and the next reconnect retries the read.\n console.error('[dsh-task-board] agent preset roster read failed', error)\n }\n }\n void pushPresetOptions()\n disposers.push(ctx.on('connection/reset', () => { void pushPresetOptions() }))\n try {\n disposers.push(mountSidebarEntry(controller))\n disposers.push(mountBoard(controller))\n // T011 §12.7/§14.4:对话候选任务提取——Agent 候选标记解析(半自动)+\n // 消息 hover「转为任务」按钮(手动兜底)。结构面适配:真实 ISessions\n // 满足 ChatSessionsFace(list/binding.session 快照订阅)。\n disposers.push(mountChatToTaskBoard(controller, sessions as unknown as ChatSessionsFace))\n disposers.push(mountMessageConvertButton(controller, sessions as unknown as ChatSessionsFace))\n } catch (error) {\n // DOM failures degrade the board, never the GUI.\n console.error('[dsh-task-board] mount failed:', error)\n }\n\n uiDisposer = () => {\n for (const dispose of disposers.splice(0)) dispose()\n controller.dispose()\n uiDisposer = undefined\n }\n }\n const syncEnabled = (): void => {\n const snapshot = settingsScope.getSnapshot()\n const enabled = snapshot.status === 'ready'\n ? snapshot.value?.enabled ?? true\n : snapshot.status === 'unavailable'\n if (enabled) mountUi()\n else uiDisposer?.()\n }\n settingsScope.subscribe(syncEnabled)\n syncEnabled()\n}\n"],"mappings":"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"}
|