@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,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 任务状态机与流转纯函数(DSH-REQ-001 §10.1/10.2 基线,任务 controller /
|
|
3
|
+
* use-case 层)。T003 交付状态机基线:
|
|
4
|
+
*
|
|
5
|
+
* - 状态:`backlog | todo | running | done | failed` + `archivedAt` 时间戳标记;
|
|
6
|
+
* - running 由 runner 独占(用户不可移动/删除/归档 running 或带未结算执行的任务);
|
|
7
|
+
* - 归档任务只读(除恢复/删除外不可变更);非法流转拒绝(抛
|
|
8
|
+
* `TaskBoardTransitionError`,路由层转为 400 + 错误信息);
|
|
9
|
+
* - T005 执行转换:`run/rerun` 开启执行(置 running + 追加 open ExecutionRecord)、
|
|
10
|
+
* `applyAttachSession` 回填会话号、`applySettleExecution` 按结果结算回落
|
|
11
|
+
* (succeeded→done、failed→failed、cancelled→todo)——执行与结算的会话驱动
|
|
12
|
+
* 属 HostRunner(host-runner.ts),本层只做账本侧的原子状态变更;
|
|
13
|
+
* - T006 定时转换:`applySetSchedule` 设定/修改/关闭 cron(§11.2 set-schedule,
|
|
14
|
+
* enabled/cron patch;置武装时立即计算 nextRunAt;与 one-shot 互斥)、
|
|
15
|
+
* `applyScheduleRoll` 到期滚动(调度 tick 用:running 中到期只滚动不触发)、
|
|
16
|
+
* `applySkipMissedSchedules` 恢复期跳过(§13.1:错过触发点不补跑)——
|
|
17
|
+
* nextRunAt/lastTriggeredAt 为 Host 独占字段,纯函数层只做账本侧变更;
|
|
18
|
+
* - T007 一次性计划(§9.4/§11.2 set-one-shot/§13.5):`applySetOneShot` 设定/
|
|
19
|
+
* 修改/清除 one-shot(runAt 缺省或 0 = 取消;设定即清除 cron 分支,互斥 D6)、
|
|
20
|
+
* `applyMarkOneShotConsumed` 触发即消费(到点 tick 写 firedAt,防止重启后
|
|
21
|
+
* 重复触发;running 冲突/错过同样标记消费)、`applySkipMissedSchedules` 的
|
|
22
|
+
* 恢复期跳过同时覆盖 one-shot(runAt < now 且未消费 → 写 firedAt 跳过不补跑);
|
|
23
|
+
* - T008 标签/项目(§9.2/§12.8):`newTaskFromInput` 落 project/tags(缺省
|
|
24
|
+
* undefined/[])、`applyUpdateTask` 支持 project(空串清除)/tags(整体替换,
|
|
25
|
+
* normalizeTags trim/去重/丢弃空白);
|
|
26
|
+
* - T009 列内排序与跨列拖拽(§9.2 order/§10.2/§11.2 reorder + move 扩展/D10):
|
|
27
|
+
* `applyReorderTask` 调整列内顺序(order 必填有限数字,Host 按落位下标重算
|
|
28
|
+
* 同列唯一、整列重编号,不信任浏览器序号;status 须与任务当前状态一致防竞态;
|
|
29
|
+
* 可携带 project/tags 归属变更与 order 原子落账)、`applyMoveTask` 扩展可选
|
|
30
|
+
* order(目标列落位下标,缺省追加末尾;done/failed 拖回 backlog/todo 为重开,
|
|
31
|
+
* 仅改状态不触发执行);running/proposed/归档任务不可作为 reorder/move 来源。
|
|
32
|
+
* - T010 自动化收集与上下文闭环(§9.5/§14,P3):
|
|
33
|
+
* - 人工审核闸门(§14.3):自动收集任务(source=github_issue/bookmark_collector)
|
|
34
|
+
* 在 backlog 期间不可 run/rerun、不可设置任何执行安排(set-schedule/
|
|
35
|
+
* set-one-shot)——「未 promote 不可执行/不可定时」;promote 即用户 move 到
|
|
36
|
+
* todo(move 不加守卫);
|
|
37
|
+
* - 评论(§9.5 comments):`applyAddComment`(author/body/type,正文清洗);
|
|
38
|
+
* - 产物(§9.5 artifacts):`applyAddArtifact`(type/title/url/contentRef,
|
|
39
|
+
* 同 (type, contentRef|url) 去重;会话 transcript 即默认产物);
|
|
40
|
+
* - 上下文快照(§9.5 context_snapshot):`applyUpdateContextSnapshot`(部分
|
|
41
|
+
* 补丁合并,空串/空数组 = 清除对应字段;updatedAt 由 Host 写);
|
|
42
|
+
* - 自动化规则(§9.5 automation_rules):`applyUpsertAutomationRule`(武装时
|
|
43
|
+
* 立即计算 nextRunAt,与 set-schedule 同语义)、`applyDeleteAutomationRule`、
|
|
44
|
+
* `applyCollectorRoll`(tick 收集后滚动 nextRunAt/lastTriggeredAt)、
|
|
45
|
+
* `applySkipMissedAutomationRules`(恢复期错过跳过不补跑);
|
|
46
|
+
* - 收集落账(§14.1/14.2):`applyAddCollectedTasks` 逐条去重(id + 同
|
|
47
|
+
* (source, url))后追加 backlog,order 接列尾。
|
|
48
|
+
* - 全部为纯函数(无 fs、无运行时依赖、就地修改传入的账本文档后返回是否变更),
|
|
49
|
+
* 与 HostLedger.applyRequest 的变更闭包契约一致(T002 交付摘要:T003 传序列化
|
|
50
|
+
* action 与变更闭包)。
|
|
51
|
+
*
|
|
52
|
+
* T011 对话候选流转(§10.1/10.2/§11.2/§14.4,P3.1):
|
|
53
|
+
* - `applyProposeTask`:propose(对话提取/手动转任务)→ 创建 proposed 候选
|
|
54
|
+
* (source=conversation,来源会话/消息引用写 metadata;标题/描述/prompt 清洗
|
|
55
|
+
* ——剥离控制字符 + trim + 长度封顶,§16.7 安全红线;命令类字段的协议层拒绝
|
|
56
|
+
* 在 protocol.ts);
|
|
57
|
+
* - `applyConfirmTask`:confirm(确认)→ 落 backlog/todo(目标仅
|
|
58
|
+
* backlog|todo),order 接目标列尾、source 缺省补 conversation——确认是候选
|
|
59
|
+
* 进入正式看板的唯一路径(防注入闸门,§14.4);
|
|
60
|
+
* - `applyDismissTask`:dismiss(拒绝/忽略)→ 删除候选(proposed 专属路径);
|
|
61
|
+
* - 守卫:proposed 未确认不可 run/rerun(RUNNABLE_SOURCES 不含)、不可设置
|
|
62
|
+
* 任何执行安排(set-schedule/set-one-shot 拒绝)、不可归档
|
|
63
|
+
* (ARCHIVABLE_STATUSES 不含)、不可 move/reorder(来源约束 + reorder 的
|
|
64
|
+
* proposed 守卫)、不可作为拖拽来源(T009 已含);编辑(update)允许
|
|
65
|
+
* (§10.2「只读(除编辑/确认/拒绝)」)。
|
|
66
|
+
*
|
|
67
|
+
* T012 需求拆分(§11.2/§12.9/§14.5,P3.2):
|
|
68
|
+
* - `applyProposeBatch`:propose-batch(批量创建候选,需求拆分产物)——
|
|
69
|
+
* items 逐条沿用 propose 的清洗(标题/描述/prompt 控制字符 + 长度封顶,
|
|
70
|
+
* §16.7);`parentId` 必须指向账本内已存在且非自身的任务(§16.8),经
|
|
71
|
+
* assertValidParentRef 校验(存在性 + 祖先链防环 + 链长上限,防损坏账本
|
|
72
|
+
* 的既有环);**任一条目非法 → 整批拒绝**(先全部校验再统一追加,原子落账、
|
|
73
|
+
* 单 revision);条目 source=requirement、order 接 proposed 列尾;
|
|
74
|
+
* - `applyCreateRequirementParent`:拆分结算时创建父需求任务「需求:<标题>」
|
|
75
|
+
* (proposed、source=requirement,描述承载需求原文/来源与覆盖矩阵——P3 产物
|
|
76
|
+
* 表落地前存描述,§14.5);递归拆分(§12.9)时父任务以原任务为父
|
|
77
|
+
* (parentId,同样过 assertValidParentRef);children 的 parentId 由
|
|
78
|
+
* Host 结算时指向该父任务。
|
|
79
|
+
*
|
|
80
|
+
* reorder 的「分组视图」归属变更属 T009(12.10:按项目分组拖到组 G 携带
|
|
81
|
+
* project:G、拖到未分类携带 project:'';按标签分组拖到组 G 携带 tags 使 G
|
|
82
|
+
* 成为首选标签、拖到未分类移除首选标签)。
|
|
83
|
+
*/
|
|
84
|
+
import type { ArtifactType, AutomationSource, BatchProposeItem, CommentType, ExecutionRecord, ExecutionResult, LedgerDocument, NewTaskInput, ProposeTaskInput, TaskRecord, TaskStatus, TaskUpdatePatch } from './model.ts';
|
|
85
|
+
/** 用户可手动移动到的目标列(§10.2:backlog/todo 互移;done/failed 重开落回)。 */
|
|
86
|
+
export declare const MANUAL_TARGET_STATUSES: readonly TaskStatus[];
|
|
87
|
+
/** 可归档的状态(§10.2:仅已结算状态可归档)。 */
|
|
88
|
+
export declare const ARCHIVABLE_STATUSES: readonly TaskStatus[];
|
|
89
|
+
/**
|
|
90
|
+
* 手动移动的来源约束:running 由 runner 独占、proposed 候选只读(T011 的
|
|
91
|
+
* confirm/dismiss 是唯一路径),二者都不可作为 move 来源。
|
|
92
|
+
*/
|
|
93
|
+
export declare const MANUAL_MOVE_SOURCES: readonly TaskStatus[];
|
|
94
|
+
/** 状态流转守卫错误:消息直接透出为路由 400 的错误信息。 */
|
|
95
|
+
export declare class TaskBoardTransitionError extends Error {
|
|
96
|
+
constructor(message: string);
|
|
97
|
+
}
|
|
98
|
+
/** 任务是否带有未结算的执行(running 独占性的第二道判据)。 */
|
|
99
|
+
export declare function hasOpenExecution(task: TaskRecord): boolean;
|
|
100
|
+
/** 手动移动合法性:来源 ∈ {backlog,todo,done,failed} 且目标 ∈ {backlog,todo}。 */
|
|
101
|
+
export declare function canMoveManually(from: TaskStatus, to: TaskStatus): boolean;
|
|
102
|
+
/** 同状态任务的最大 order(无则 0)——新任务/跨列移动追加到目标列末尾。 */
|
|
103
|
+
export declare function maxOrderOf(tasks: readonly TaskRecord[], status: TaskStatus): number;
|
|
104
|
+
/** 从用户输入构造一条新任务(create 的落地;§9.2 字段归一化)。T008:project/tags 落字段。 */
|
|
105
|
+
export declare function newTaskFromInput(input: NewTaskInput, id: string, now: number, order: number): TaskRecord;
|
|
106
|
+
/**
|
|
107
|
+
* create(§11.2):新任务默认进 todo,追加到列末尾(order = 同列 max+1)。
|
|
108
|
+
* 守卫:id 唯一、标题 trim 后非空。返回是否发生变更(恒 true)。
|
|
109
|
+
*/
|
|
110
|
+
export declare function applyCreateTask(doc: LedgerDocument, input: NewTaskInput, id: string, now: number): boolean;
|
|
111
|
+
/** 标题长度上限(§16.7 清洗:对齐自动收集的标题封顶)。 */
|
|
112
|
+
export declare const PROPOSE_TITLE_LIMIT = 200;
|
|
113
|
+
/** 描述/prompt 长度上限(§16.7 清洗:对齐评论正文封顶)。 */
|
|
114
|
+
export declare const PROPOSE_TEXT_LIMIT = 4000;
|
|
115
|
+
/**
|
|
116
|
+
* propose(§11.2/§14.4,T011):对话提取/手动转任务 → 创建 proposed 候选任务。
|
|
117
|
+
*
|
|
118
|
+
* 守卫:id 唯一、清洗后标题非空。
|
|
119
|
+
* 清洗(§16.7 安全红线):title/description/prompt 剥离控制字符 + trim + 长度
|
|
120
|
+
* 封顶(sanitizeCollectedText,与自动收集同一条红线);命令类字段的协议层拒绝
|
|
121
|
+
* 在 protocol.ts(hasForbiddenCommandFields)。
|
|
122
|
+
* 数据(§9.2/§14.4):status=proposed、source=conversation、来源会话/消息引用
|
|
123
|
+
* 写入 metadata(sourceConversationId/sourceMessageId,可跳回对话);order 追加
|
|
124
|
+
* proposed 列尾;project/tags 沿用 create 的归一化语义。
|
|
125
|
+
*/
|
|
126
|
+
export declare function applyProposeTask(doc: LedgerDocument, input: ProposeTaskInput, id: string, now: number): boolean;
|
|
127
|
+
/** confirm 目标(§10.2/§11.2:仅 backlog | todo)。 */
|
|
128
|
+
export type ConfirmTarget = 'backlog' | 'todo';
|
|
129
|
+
/**
|
|
130
|
+
* confirm(§10.2/§11.2,T011):确认候选任务 → 落列(backlog/todo)。
|
|
131
|
+
* 守卫:任务存在、非归档、status=proposed、target ∈ {backlog, todo}。
|
|
132
|
+
* 落列后:status=target、order 追加目标列尾(max+1)、source 缺省补
|
|
133
|
+
* 'conversation'(§10.2「确认后 source 置 conversation」;propose 落账时已置,
|
|
134
|
+
* 这里只兜底陈旧数据);来源引用 metadata 保留(可跳回对话)。confirm 是候选
|
|
135
|
+
* 进入正式看板的唯一路径(防注入闸门,§14.4/§16.7)。
|
|
136
|
+
*/
|
|
137
|
+
export declare function applyConfirmTask(doc: LedgerDocument, taskId: string, target: ConfirmTarget, now: number): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* dismiss(§10.2/§11.2,T011):拒绝/忽略候选任务 → 删除。
|
|
140
|
+
* 守卫:任务存在、非归档、status=proposed——dismiss 是 proposed 专属路径
|
|
141
|
+
* (删除正式任务请用 delete)。
|
|
142
|
+
*/
|
|
143
|
+
export declare function applyDismissTask(doc: LedgerDocument, taskId: string): boolean;
|
|
144
|
+
/** parentId 祖先链长度上限(§16.8 防环安全阀:超过即视为环/异常链拒绝)。 */
|
|
145
|
+
export declare const MAX_PARENT_CHAIN = 64;
|
|
146
|
+
/**
|
|
147
|
+
* parentId 引用校验(§16.8):指向的任务必须存在于账本中(`非自身`由调用方
|
|
148
|
+
* 保证——本批条目的 id 为新建 uuid,不可能等于已有任务 id;递归父任务的自身
|
|
149
|
+
* 引用同样在创建前生成新 id),并沿祖先链向上走查环:
|
|
150
|
+
* - 链中出现本批新条目 id(newIds)或已访问任务 → 环引用,拒绝(防御账本
|
|
151
|
+
* 既有损坏环 / 未来允许批内引用时的自环);
|
|
152
|
+
* - 链上任务缺失 → 非法父引用,拒绝;
|
|
153
|
+
* - 链长超过 MAX_PARENT_CHAIN → 视为异常链,拒绝。
|
|
154
|
+
*/
|
|
155
|
+
export declare function assertValidParentRef(doc: LedgerDocument, parentId: string, newIds?: ReadonlySet<string>): void;
|
|
156
|
+
/**
|
|
157
|
+
* propose-batch(§11.2/§14.5/§16.8,T012):批量创建候选任务(需求拆分产物)。
|
|
158
|
+
*
|
|
159
|
+
* **原子性**:先全部校验(标题清洗后非空 + parentId 引用合法性),任一条目
|
|
160
|
+
* 非法 → 抛 `TaskBoardTransitionError`,**整批拒绝、账本零变更**;全部合法后
|
|
161
|
+
* 统一追加(单 revision)。id 由本函数生成(uuid,防客户端碰撞/自引用)。
|
|
162
|
+
*
|
|
163
|
+
* 数据:status=proposed、source=requirement、来源会话/消息引用写 metadata、
|
|
164
|
+
* order 接 proposed 列尾、project/tags 沿用 create 归一化语义;命令类字段的
|
|
165
|
+
* 协议层拒绝在 protocol.ts(hasForbiddenCommandFields)。
|
|
166
|
+
*/
|
|
167
|
+
export declare function applyProposeBatch(doc: LedgerDocument, items: readonly BatchProposeItem[], now: number): boolean;
|
|
168
|
+
/** 父需求任务输入(§14.5:Host 拆分结算时构造)。 */
|
|
169
|
+
export interface RequirementParentInput {
|
|
170
|
+
/** 需求标题(「需求:<标题>」)。 */
|
|
171
|
+
title: string;
|
|
172
|
+
/** 需求原文 / 来源说明(写入描述,§14.5 承载需求原文与覆盖矩阵)。 */
|
|
173
|
+
requirementText: string;
|
|
174
|
+
/** 覆盖矩阵(写入描述;可空)。 */
|
|
175
|
+
coverage?: string;
|
|
176
|
+
/** 需求所属项目。 */
|
|
177
|
+
project?: string;
|
|
178
|
+
/** 默认标签。 */
|
|
179
|
+
tags?: string[];
|
|
180
|
+
/** 递归拆分:父任务挂到该任务下(§12.9;必须存在且非自身)。 */
|
|
181
|
+
parentId?: string;
|
|
182
|
+
/** 拆分会话引用(写入 metadata,可跳回追溯,§15)。 */
|
|
183
|
+
sourceConversationId?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 创建父需求任务(§14.5,T012):`需求:<标题>`,status=proposed、
|
|
187
|
+
* source=requirement,描述 = 需求原文/来源 + 覆盖矩阵(P3 产物表落地前存
|
|
188
|
+
* 描述),order 接 proposed 列尾。递归拆分(§12.9)时 `parentId` 指向原任务
|
|
189
|
+
* (assertValidParentRef 校验);`parentId` 创建后不可改(update 补丁面不含)。
|
|
190
|
+
* 守卫:id 唯一;清洗后标题非空。
|
|
191
|
+
*/
|
|
192
|
+
export declare function applyCreateRequirementParent(doc: LedgerDocument, input: RequirementParentInput, parentId: string, now: number): boolean;
|
|
193
|
+
/**
|
|
194
|
+
* update(§11.2):可编辑字段补丁(标题/描述/prompt/工作区/模式/权限/项目/标签)。
|
|
195
|
+
* 守卫:任务存在、非归档。空字符串清除钉住字段(workspaceId/mode/project);
|
|
196
|
+
* 未知权限串忽略(陈旧 UI 不得持久化执行服务拒绝的值);标题 trim 后非空。
|
|
197
|
+
* T008:`project` 空串清除(normalizeOptionalString)、`tags` 整体替换
|
|
198
|
+
* (normalizeTags:trim/去重/丢弃空白,空数组 = 清空)。
|
|
199
|
+
*/
|
|
200
|
+
export declare function applyUpdateTask(doc: LedgerDocument, taskId: string, patch: TaskUpdatePatch, now: number): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* delete(§10.2/§19-8):守卫——任务存在、非 running、无未结算执行。
|
|
203
|
+
* 归档任务可删除(归档只读的例外:恢复/删除/看 transcript)。
|
|
204
|
+
*/
|
|
205
|
+
export declare function applyDeleteTask(doc: LedgerDocument, taskId: string): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* 列内排序的稳定比较器(§9.2 order 升序;order 相等时按 createdAt/id 决胜,
|
|
208
|
+
* 兼容 T009 前的重复 order 历史数据)。客户端分组视图的排序必须与本函数
|
|
209
|
+
* 一致(grouping.ts sortByOrder),否则浏览器计算的落位下标与 Host 重算不一致。
|
|
210
|
+
*/
|
|
211
|
+
export declare function byOrderStable(a: TaskRecord, b: TaskRecord): number;
|
|
212
|
+
/**
|
|
213
|
+
* move(§10.2/§11.2 基线 + T009 order 扩展):跨列移动/跨列拖拽。守卫——任务
|
|
214
|
+
* 存在、非归档、非 running/无未结算执行、来源与目标合法(backlog/todo 互移、
|
|
215
|
+
* done/failed 重开;proposed 与 running 不可作为来源)。目标列与当前列不同时
|
|
216
|
+
* 追加到目标列末尾(order 由 Host 重算,不信任浏览器序号);T009 起 `order`
|
|
217
|
+
* 可选:提供时为目标列落位下标(0-based,整列重编号),缺省追加末尾;同状态
|
|
218
|
+
* move 保持 order(沿用基线行为,仅 bump updatedAt)。
|
|
219
|
+
*/
|
|
220
|
+
export declare function applyMoveTask(doc: LedgerDocument, taskId: string, status: TaskStatus, now: number, order?: number): boolean;
|
|
221
|
+
/**
|
|
222
|
+
* reorder(§11.2/D10,T009):列内排序/分组落位。守卫——任务存在、非归档、
|
|
223
|
+
* 非 running/无未结算执行、`status` 与任务当前状态一致(防竞态——reorder 只
|
|
224
|
+
* 调整列内 order,绝不改状态,跨列走 move)。`order` 必填有限数字,语义为
|
|
225
|
+
* 0-based 落位下标(目标列按 byOrderStable 排序后插入,Host 整列重编号保证
|
|
226
|
+
* 同列唯一,不信任浏览器序号)。可选 `ownership`(project/tags)为分组视图
|
|
227
|
+
* 跨组落位的归属变更(§12.10):`project` 空串 = 清除归属、`tags` 整体替换
|
|
228
|
+
* (沿用 applyUpdateTask 的 normalizeOptionalString/normalizeTags 语义),与
|
|
229
|
+
* order 原子落账。
|
|
230
|
+
*/
|
|
231
|
+
export declare function applyReorderTask(doc: LedgerDocument, taskId: string, status: TaskStatus, order: number, ownership: {
|
|
232
|
+
project?: string;
|
|
233
|
+
tags?: string[];
|
|
234
|
+
} | undefined, now: number): boolean;
|
|
235
|
+
/**
|
|
236
|
+
* archive(§10.2/§19-6):仅已结算状态(done/failed)可归档;归档自动解除
|
|
237
|
+
* 定时——cron 分支解除武装(enabled:false、清 nextRunAt,保留 cron 配置便于
|
|
238
|
+
* 恢复后重新武装),one-shot 分支整体清除(§9.4:归档时自动清除 one-shot
|
|
239
|
+
* 计划)。已归档任务重复归档为 no-op。
|
|
240
|
+
*/
|
|
241
|
+
export declare function applyArchiveTask(doc: LedgerDocument, taskId: string, now: number): boolean;
|
|
242
|
+
/**
|
|
243
|
+
* restore(§10.2):恢复归档任务回主看板(原状态列),保留执行历史;定时保持
|
|
244
|
+
* 解除状态(恢复前不可运行,需用户重新武装——T006 联调)。非归档任务抛错。
|
|
245
|
+
*/
|
|
246
|
+
export declare function applyRestoreTask(doc: LedgerDocument, taskId: string, now: number): boolean;
|
|
247
|
+
/** run/rerun 允许的来源状态(§10.2/§13.2):非 running、非 proposed 候选、非归档。 */
|
|
248
|
+
export declare const RUNNABLE_SOURCES: readonly TaskStatus[];
|
|
249
|
+
/**
|
|
250
|
+
* run/rerun 共同守卫(§13.2):任务非归档、非 running、无未结算执行、来源在
|
|
251
|
+
* RUNNABLE_SOURCES(proposed 候选不可执行——确认闸门是唯一 promote 路径,T011;
|
|
252
|
+
* T010:自动收集任务在 backlog 期间同样不可执行——人工审核闸门,§14.3)。
|
|
253
|
+
*/
|
|
254
|
+
export declare function canRunTask(task: TaskRecord): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* 开启一条新执行(§13.2):任务置 running + 追加 open ExecutionRecord
|
|
257
|
+
* (startedAt=now;sessionId/endedAt/result 待创建会话后回填/结算)。
|
|
258
|
+
* 返回新任务与执行记录(HostRunner 据此启动会话)。
|
|
259
|
+
*/
|
|
260
|
+
export declare function openExecution(task: TaskRecord, now: number, executionId: string): {
|
|
261
|
+
task: TaskRecord;
|
|
262
|
+
execution: ExecutionRecord;
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* run(§13.1 手动执行 / §13.2 起点):开启执行(置 running + 追加 open 执行
|
|
266
|
+
* 记录)。守卫:任务存在、非归档、非 running、无未结算执行、来源合法。
|
|
267
|
+
*/
|
|
268
|
+
export declare function applyRunTask(doc: LedgerDocument, taskId: string, now: number): boolean;
|
|
269
|
+
/**
|
|
270
|
+
* rerun(§13.2 重新执行):先落回 todo(重开语义——done/failed 重跑不改历史
|
|
271
|
+
* 状态,见 §12.10「拖回重开」),再按 run 开启执行。守卫同 run。
|
|
272
|
+
*/
|
|
273
|
+
export declare function applyRerunTask(doc: LedgerDocument, taskId: string, now: number): boolean;
|
|
274
|
+
/**
|
|
275
|
+
* attachSession(§13.2 会话回填):把创建的 DSH 会话 id 回填到指定执行记录。
|
|
276
|
+
* 执行不存在抛错;已结算记录也允许回填(崩溃后会话号晚到仍保留跳转线索,
|
|
277
|
+
* 对齐参考实现 attachSession)。同值回填为 no-op。
|
|
278
|
+
*/
|
|
279
|
+
export declare function applyAttachSession(doc: LedgerDocument, taskId: string, executionId: string, sessionId: string, now: number): boolean;
|
|
280
|
+
/** 结算结果 → 任务回落状态(§13.3):succeeded→done、failed→failed、cancelled→todo。 */
|
|
281
|
+
export declare function settleStatusOf(result: ExecutionResult, current: TaskStatus): TaskStatus;
|
|
282
|
+
/**
|
|
283
|
+
* settleExecution(§13.3):结算一条运行中的执行——写 endedAt/result/error,
|
|
284
|
+
* 任务按结果回落列(succeeded→done、failed→failed、cancelled→todo)。
|
|
285
|
+
* 守卫:执行存在;已结算执行为 no-op(返回 false,不 bump revision)。
|
|
286
|
+
*/
|
|
287
|
+
export declare function applySettleExecution(doc: LedgerDocument, taskId: string, executionId: string, result: ExecutionResult, now: number, error?: string): boolean;
|
|
288
|
+
/** set-schedule 补丁(§11.2):只允许 enabled/cron 两个浏览器可写字段。 */
|
|
289
|
+
export interface SetSchedulePatch {
|
|
290
|
+
enabled?: boolean;
|
|
291
|
+
cron?: string;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* set-schedule(§11.2 / §9.4 cron 分支):设定/修改/关闭任务的 cron 周期定时。
|
|
295
|
+
*
|
|
296
|
+
* - 守卫:任务存在、非归档、非 proposed(§10.2:候选任务不可设置任何执行安排);
|
|
297
|
+
* - cron 取 `patch.cron ?? 当前 cron`;空/语法非法抛错(§11.2 cron 合法性;
|
|
298
|
+
* 状态机层再校验,Host 不信任浏览器预检);
|
|
299
|
+
* - enabled 取 `patch.enabled ?? 当前 enabled ?? false`;置武装(true)时立即计算
|
|
300
|
+
* nextRunAt(`nextRunAtMs(cron, now)`),不可能匹配的表达式(如 `0 0 30 2 *`)
|
|
301
|
+
* 抛错;解除武装只清 nextRunAt、保留 cron 配置(归档同语义,见 applyArchiveTask);
|
|
302
|
+
* - 与 one-shot 互斥(D6):无论 patch 内容,结果恒为 cron 分支,原 one-shot
|
|
303
|
+
* 整体清除;
|
|
304
|
+
* - 无实际变更(同值 patch)返回 false,不 bump revision。
|
|
305
|
+
*/
|
|
306
|
+
export declare function applySetSchedule(doc: LedgerDocument, taskId: string, patch: SetSchedulePatch, now: number): boolean;
|
|
307
|
+
/**
|
|
308
|
+
* 到期滚动(调度 tick 用,§13.1):把一条 cron 规则滚动到下一个匹配点。
|
|
309
|
+
* - 任务不存在/已归档/无 schedule/非 cron 分支 → no-op(返回 false);
|
|
310
|
+
* - 写 nextRunAt 与 lastTriggeredAt(Host 独占字段);nextRunAt 传 undefined
|
|
311
|
+
* 表示清空(不可能匹配的表达式滚动后不再到期);lastTriggeredAt 传 undefined
|
|
312
|
+
* 表示保留现值(running 中到期只滚动不触发,本次不记触发点);
|
|
313
|
+
* - 同值滚动为 no-op(不 bump revision)。
|
|
314
|
+
*/
|
|
315
|
+
export declare function applyScheduleRoll(doc: LedgerDocument, taskId: string, nextRunAt: number | undefined, lastTriggeredAt: number | undefined, now: number): boolean;
|
|
316
|
+
/**
|
|
317
|
+
* 恢复期跳过(§13.1:错过触发点跳过不补跑):Host 停机/睡眠/长暂停后首个 tick
|
|
318
|
+
* 对全部到期的武装 cron 规则执行——不触发执行,直接把 nextRunAt 滚动到
|
|
319
|
+
* `nextRunAtMs(cron, now)`(now 之后的下一个匹配点)。无实际变更返回 false。
|
|
320
|
+
*
|
|
321
|
+
* T007 扩展:one-shot 分支同样消费——runAt ≤ now 且未消费(firedAt 缺省)的
|
|
322
|
+
* 一次性计划写 `firedAt = now` 标记为已错过(§13.5 错过恢复:跳过不补跑,
|
|
323
|
+
* UI 展示「已过期/已跳过」),但不触发执行。
|
|
324
|
+
*/
|
|
325
|
+
export declare function applySkipMissedSchedules(doc: LedgerDocument, now: number): boolean;
|
|
326
|
+
/** set-one-shot 补丁(§11.2):只允许 runAt 一个浏览器可写字段(缺省/0 = 取消)。 */
|
|
327
|
+
export interface SetOneShotPatch {
|
|
328
|
+
runAt?: number;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* set-one-shot(§11.2 / §9.4 one-shot 分支):设定/修改/清除任务的一次性计划。
|
|
332
|
+
*
|
|
333
|
+
* - 守卫:任务存在、非归档、非 proposed(§10.2:候选任务不可设置任何执行安排);
|
|
334
|
+
* - `runAt` 缺省或 0 → 清除 one-shot 计划(整条 schedule 删除;与 cron 分支
|
|
335
|
+
* 无关——清除 one-shot 不触碰 cron,设定才互斥);
|
|
336
|
+
* - `runAt` 为有限数 → 设定 `{ kind: 'one-shot', runAt }`,与 cron 分支互斥
|
|
337
|
+
* (D6):无论账本当前是 cron 还是 one-shot,结果恒为 one-shot 分支,原
|
|
338
|
+
* cron 整体清除;
|
|
339
|
+
* - 无实际变更(同值 patch)返回 false,不 bump revision。
|
|
340
|
+
*/
|
|
341
|
+
export declare function applySetOneShot(doc: LedgerDocument, taskId: string, patch: SetOneShotPatch, now: number): boolean;
|
|
342
|
+
/**
|
|
343
|
+
* 触发即消费(§13.5 tick 用):把一条到点的一次性计划标记为已消费(写
|
|
344
|
+
* `firedAt = now`),防止重启后重复触发。
|
|
345
|
+
* - 任务不存在/已归档/无 schedule/非 one-shot 分支/已消费 → no-op(false);
|
|
346
|
+
* - 同值消费(firedAt 已等于 now)为 no-op(不 bump revision)。
|
|
347
|
+
* 与 cron 的滚动(applyScheduleRoll)相对:one-shot 只触发一次,消费即终态。
|
|
348
|
+
*/
|
|
349
|
+
export declare function applyMarkOneShotConsumed(doc: LedgerDocument, taskId: string, now: number): boolean;
|
|
350
|
+
/** add-comment 输入(§11.2;author/type 缺省 'user'/'user_feedback')。 */
|
|
351
|
+
export interface AddCommentInput {
|
|
352
|
+
author?: string;
|
|
353
|
+
body: string;
|
|
354
|
+
type?: CommentType;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* add-comment(§9.5 comments):任务存在、非归档;正文清洗(控制字符 + 长度
|
|
358
|
+
* 封顶 4000)后追加;作者缺省 'user'、类型缺省 'user_feedback'(user_feedback/
|
|
359
|
+
* ai_log/system_event 三型,§9.5)。
|
|
360
|
+
*/
|
|
361
|
+
export declare function applyAddComment(doc: LedgerDocument, taskId: string, input: AddCommentInput, now: number): boolean;
|
|
362
|
+
/** add-artifact 输入(§11.2)。 */
|
|
363
|
+
export interface AddArtifactInput {
|
|
364
|
+
type: ArtifactType;
|
|
365
|
+
title: string;
|
|
366
|
+
url?: string;
|
|
367
|
+
contentRef?: string;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* add-artifact(§9.5 artifacts):任务存在、非归档;标题清洗后非空、类型合法;
|
|
371
|
+
* 同 (type, contentRef) 或 (type, url) 视为同一条产物(会话产物防重),返回
|
|
372
|
+
* false(不 bump revision)。会话 transcript 由 Host 在执行结算成功时自动记录
|
|
373
|
+
* (type='session',contentRef=sessionId)。
|
|
374
|
+
*/
|
|
375
|
+
export declare function applyAddArtifact(doc: LedgerDocument, taskId: string, input: AddArtifactInput, now: number): boolean;
|
|
376
|
+
/** update-context 补丁(§11.2):可编辑字段全集;空串/空数组 = 清除对应字段。 */
|
|
377
|
+
export interface ContextSnapshotPatch {
|
|
378
|
+
goal?: string;
|
|
379
|
+
keyDecisions?: string[];
|
|
380
|
+
filePaths?: string[];
|
|
381
|
+
lastAiSummary?: string;
|
|
382
|
+
latestUserFeedback?: string;
|
|
383
|
+
relatedLinks?: string[];
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* update-context(§9.5 context_snapshot):部分补丁合并进既有快照(缺省字段
|
|
387
|
+
* 保持现值);字符串清洗、数组 trim/去重/封顶;空串/空数组清除对应字段;
|
|
388
|
+
* updatedAt 由 Host 写(now)。无实际内容变更返回 false(不 bump revision)。
|
|
389
|
+
* 每次执行后由 Host 以 contextPatchFromSettle 合成更新(host-service.ts)。
|
|
390
|
+
*/
|
|
391
|
+
export declare function applyUpdateContextSnapshot(doc: LedgerDocument, taskId: string, patch: ContextSnapshotPatch, now: number): boolean;
|
|
392
|
+
/** upsert-automation 输入(§11.2;nextRunAt/lastTriggeredAt 为 Host 独占字段)。 */
|
|
393
|
+
export interface AutomationRuleInput {
|
|
394
|
+
id: string;
|
|
395
|
+
enabled: boolean;
|
|
396
|
+
source: AutomationSource;
|
|
397
|
+
trigger: {
|
|
398
|
+
kind: 'cron';
|
|
399
|
+
cron: string;
|
|
400
|
+
};
|
|
401
|
+
config: Record<string, string>;
|
|
402
|
+
filter: {
|
|
403
|
+
labels: string[];
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* upsert-automation(§9.5 automation_rules):创建或整条替换一条规则。
|
|
408
|
+
* - 校验:id 非空、source 合法、cron 语法合法(不可匹配的表达式抛错);
|
|
409
|
+
* - 武装(enabled=true)时立即计算 nextRunAt;解除武装清 nextRunAt、保留 cron
|
|
410
|
+
* 配置(与 set-schedule 同语义);
|
|
411
|
+
* - lastTriggeredAt 从既有规则延续(Host 独占字段);同内容 upsert 为 no-op。
|
|
412
|
+
*/
|
|
413
|
+
export declare function applyUpsertAutomationRule(doc: LedgerDocument, input: AutomationRuleInput, now: number): boolean;
|
|
414
|
+
/** delete-automation(§11.2):规则不存在抛错。 */
|
|
415
|
+
export declare function applyDeleteAutomationRule(doc: LedgerDocument, ruleId: string): boolean;
|
|
416
|
+
/**
|
|
417
|
+
* 收集器滚动(tick/手动触发用,§14.2):写规则 trigger 的 nextRunAt 与
|
|
418
|
+
* lastTriggeredAt(Host 独占字段)。nextRunAt 传 undefined 表示保留现值
|
|
419
|
+
* (手动 run-automation 不消费计划触发点);lastTriggeredAt 传 undefined 表示
|
|
420
|
+
* 保留现值。同值滚动为 no-op。
|
|
421
|
+
*/
|
|
422
|
+
export declare function applyCollectorRoll(doc: LedgerDocument, ruleId: string, nextRunAt: number | undefined, lastTriggeredAt: number | undefined, now: number): boolean;
|
|
423
|
+
/**
|
|
424
|
+
* 恢复期跳过(§14.2,与任务 cron 的 applySkipMissedSchedules 同语义):Host
|
|
425
|
+
* 停机/睡眠/长暂停后首个 tick 对全部到期的武装规则——不触发收集,直接把
|
|
426
|
+
* nextRunAt 滚动到 now 之后的下一个匹配点(错过不补跑)。
|
|
427
|
+
*/
|
|
428
|
+
export declare function applySkipMissedAutomationRules(doc: LedgerDocument, now: number): boolean;
|
|
429
|
+
/**
|
|
430
|
+
* 收集落账:逐条把收集到的任务追加为 backlog。守卫:
|
|
431
|
+
* - id 已存在 → 跳过(幂等);
|
|
432
|
+
* - 账本中已有同 (source, metadata.url) 的任务 → 跳过(去重,§14.1/14.2);
|
|
433
|
+
* - order 由 Host 接 backlog 列尾重算(不信任收集器传入的 order)。
|
|
434
|
+
* 返回是否发生实际变更。
|
|
435
|
+
*/
|
|
436
|
+
export declare function applyAddCollectedTasks(doc: LedgerDocument, tasks: readonly TaskRecord[]): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH_HOME 解析(AGENTS.md §1.2 / D2:本项目账本目录
|
|
3
|
+
* `$DSH_HOME/nova-ui/task-board/`,避开宿主 `$DSH_HOME/task-board/`)。
|
|
4
|
+
*
|
|
5
|
+
* 环境变量 `DSH_HOME` 优先(支持 `~` 展开与相对路径按 cwd 解析),否则回退
|
|
6
|
+
* 平台家目录下的 `.dsh`。与宿主 dsh 系列插件的解析约定一致。
|
|
7
|
+
*/
|
|
8
|
+
/** 展开路径前导 `~`(或 `~user`)。 */
|
|
9
|
+
export declare function expandHome(path: string, home?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* 解析 DSH 家目录。
|
|
12
|
+
* @param env - 读取 DSH_HOME 的环境(测试可注入)。
|
|
13
|
+
* @param home - 平台家目录回退(测试可注入)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveDshHome(env?: NodeJS.ProcessEnv, home?: string): string;
|
|
16
|
+
/** 从当前环境解析 DSH 家目录。 */
|
|
17
|
+
export declare function dshHome(): string;
|
|
18
|
+
/** 本项目账本目录(相对 DSH_HOME 的子路径,见 AGENTS.md D2)。 */
|
|
19
|
+
export declare const LEDGER_DIR_RELATIVE: string;
|
|
20
|
+
/** 本项目账本目录的绝对路径。 */
|
|
21
|
+
export declare function ledgerDir(): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动化收集器(DSH-REQ-001 §14.1/14.2,T010/P3):把外部内容拉取为标准化
|
|
3
|
+
* 的 backlog `TaskRecord`(去重键、清洗与映射在 core/context.ts,本模块只做
|
|
4
|
+
* I/O 与编排)。
|
|
5
|
+
*
|
|
6
|
+
* 两个收集源:
|
|
7
|
+
* - **bookmark_collector(§14.2 收藏收集)**:读取本地「收藏收件箱」文件
|
|
8
|
+
* (JSONL:每行 `{"title","url","note"}`),逐条映射为 backlog 任务。
|
|
9
|
+
* 默认文件 `$DSH_HOME/nova-ui/task-board/bookmarks.jsonl`(规则 config 的
|
|
10
|
+
* `filePath` 可覆盖);浏览器侧导出书签/手动追加均可(B4 调研:浏览器
|
|
11
|
+
* 书签 API 需扩展能力,v1.0 由「收件箱文件 + 人工维护」承载)。
|
|
12
|
+
* - **github_issue(§14.1 GitHub Issue → backlog)**:经 GitHub REST API 拉取
|
|
13
|
+
* 仓库 open issues(规则 config 的 `repo`),按 `filter.labels` 过滤(非空时
|
|
14
|
+
* Issue 必须命中至少一个标签),跳过 PR。Webhook 推送是另一入口
|
|
15
|
+
* (host-routes.ts `POST /api/nova-task-board/webhooks/github`,HMAC 校验)。
|
|
16
|
+
*
|
|
17
|
+
* 依赖面(§17 可测试性):fetch/readFile 结构面注入(测试注入 fake);token
|
|
18
|
+
* 从环境解析(不落配置/账本)。收集结果经 transitions.applyAddCollectedTasks
|
|
19
|
+
* 原子落账(同 (source, url) 去重)。
|
|
20
|
+
*/
|
|
21
|
+
import type { AutomationRule, TaskRecord } from './core/model.ts';
|
|
22
|
+
/** 收集器依赖面(§17 可测试性:全部可注入 fake)。 */
|
|
23
|
+
export interface TaskBoardAutomationDeps {
|
|
24
|
+
/** GitHub API token(env 解析;undefined = 匿名限速 60 次/时)。 */
|
|
25
|
+
githubToken?: string;
|
|
26
|
+
/** fetch 实现(默认 globalThis.fetch;测试注入 fake)。 */
|
|
27
|
+
fetchImpl?: typeof fetch;
|
|
28
|
+
/** 书签收件箱默认目录(默认账本目录,见 host-service 注入)。 */
|
|
29
|
+
bookmarkDir?: string;
|
|
30
|
+
/** 文件读取(默认 readFileSync;测试注入内存 fake)。undefined = 不可读/不存在。 */
|
|
31
|
+
readFile?: (file: string) => string | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** 一次收集的产物:标准化任务列表 + 可见错误。 */
|
|
34
|
+
export interface CollectedBatch {
|
|
35
|
+
tasks: TaskRecord[];
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
/** 书签收件箱默认文件名(§14.2)。 */
|
|
39
|
+
export declare const BOOKMARK_INBOX_FILE = "bookmarks.jsonl";
|
|
40
|
+
/**
|
|
41
|
+
* 自动化收集器:两个收集源的 I/O 实现。无状态、无计时器——由
|
|
42
|
+
* TaskBoardHostService 在 tick / 手动触发 / Webhook 时调用。
|
|
43
|
+
*/
|
|
44
|
+
export declare class TaskBoardAutomationCollector {
|
|
45
|
+
private readonly deps;
|
|
46
|
+
constructor(deps?: TaskBoardAutomationDeps);
|
|
47
|
+
private readFile;
|
|
48
|
+
/**
|
|
49
|
+
* 收藏收集(§14.2):读收件箱 JSONL → 逐条映射 backlog 任务。
|
|
50
|
+
* 文件缺失/不可读 → error(不产出任务)。去重(同 url)由落账层完成。
|
|
51
|
+
*/
|
|
52
|
+
collectBookmarks(rule: AutomationRule, now: number): CollectedBatch;
|
|
53
|
+
/**
|
|
54
|
+
* GitHub Issue 收集(§14.1 轮询路径):`GET /repos/{repo}/issues?state=open`,
|
|
55
|
+
* 跳过 PR、按规则标签过滤;token 缺省时匿名请求。网络/HTTP 失败 → error
|
|
56
|
+
* (Host 记入可见错误,不产出任务)。
|
|
57
|
+
*/
|
|
58
|
+
collectGithubIssues(rule: AutomationRule, now: number): Promise<CollectedBatch>;
|
|
59
|
+
}
|