@william2000/dsh-nova-ui-task-board 0.1.1 → 0.2.1

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/lib/index.js CHANGED
@@ -465,6 +465,19 @@ var HostExecutionRunner = class {
465
465
  };
466
466
  }
467
467
  /**
468
+ * 停止底层会话(§13.3 用户取消执行):尽力而为——RPC 失败/被拒/会话不存在
469
+ * 都只记日志,不抛给控制面(账本侧 cancelled 结算已权威,本调用只是避免
470
+ * 孤儿 agent 继续空转/烧额度)。
471
+ */
472
+ async cancelSession(sessionId) {
473
+ try {
474
+ const response = await this.api.sessions.cancel(request({ sessionId }));
475
+ if (!response.result.ok) console.warn(`[dsh-task-board] session cancel failed (best-effort): ${response.result.error.code}: ${response.result.error.message}`);
476
+ } catch (error) {
477
+ console.warn(`[dsh-task-board] session cancel failed (best-effort): ${error instanceof Error ? error.message : String(error)}`);
478
+ }
479
+ }
480
+ /**
468
481
  * 读取会话 transcript 全文(T012/B8:拆分会话结果解析用)。翻页读取历史事件,
469
482
  * 拼接全部 assistant 消息文本(顺序拼接、换行分隔;总长封顶
470
483
  * TRANSCRIPT_TEXT_LIMIT)。会话不存在/历史不可读 → undefined(调用方按失败
@@ -1802,6 +1815,28 @@ function parseActionEnvelope(value) {
1802
1815
  taskId
1803
1816
  }
1804
1817
  };
1818
+ case "cancel-execution":
1819
+ if (!exactKeys(action, [
1820
+ "kind",
1821
+ "taskId",
1822
+ "executionId"
1823
+ ])) return void 0;
1824
+ if (taskId === void 0) return void 0;
1825
+ if (action.executionId !== void 0 && (typeof action.executionId !== "string" || action.executionId.trim() === "")) return;
1826
+ return action.executionId === void 0 ? {
1827
+ requestId: envelope.requestId,
1828
+ action: {
1829
+ kind: "cancel-execution",
1830
+ taskId
1831
+ }
1832
+ } : {
1833
+ requestId: envelope.requestId,
1834
+ action: {
1835
+ kind: "cancel-execution",
1836
+ taskId,
1837
+ executionId: action.executionId
1838
+ }
1839
+ };
1805
1840
  case "add-comment":
1806
1841
  if (!exactKeys(action, [
1807
1842
  "kind",
@@ -2945,6 +2980,27 @@ function applySettleExecution(doc, taskId, executionId, result, now, error) {
2945
2980
  doc.tasks = doc.tasks.map((candidate) => candidate.id === taskId ? next : candidate);
2946
2981
  return true;
2947
2982
  }
2983
+ /**
2984
+ * cancel-execution(§11.2,用户手动停止执行):把一条未结算执行结算为
2985
+ * cancelled(endedAt=now、result=cancelled、error 注明用户取消),任务按
2986
+ * settleStatusOf 回落(cancelled → todo)——running 独占随即释放,任务恢复
2987
+ * 可移动/可删除/可归档。这是 running 任务「停不下来」的唯一解锁路径:即使
2988
+ * 底层会话已停滞(无 turn/end、会话列表仍在),账本侧结算仍然权威生效。
2989
+ *
2990
+ * 守卫:任务存在;无未结算执行 → 抛错('task has no running execution');
2991
+ * `executionId` 缺省取最后一条未结算执行,提供时须是该任务的未结算执行
2992
+ * (已结算执行不可作为取消目标,否则 'execution not found')。
2993
+ * 底层会话的停止由 Host 尽力而为(runner.cancelSession),本层不触碰会话。
2994
+ */
2995
+ function applyCancelExecution(doc, taskId, executionId, now) {
2996
+ const task = doc.tasks.find((candidate) => candidate.id === taskId);
2997
+ if (task === void 0) throw new TaskBoardTransitionError("task not found");
2998
+ const open = task.executions.filter((execution) => execution.endedAt === void 0);
2999
+ if (open.length === 0) throw new TaskBoardTransitionError("task has no running execution");
3000
+ const target = executionId === void 0 ? open[open.length - 1] : open.find((execution) => execution.id === executionId);
3001
+ if (target === void 0) throw new TaskBoardTransitionError("execution not found");
3002
+ return applySettleExecution(doc, taskId, target.id, "cancelled", now, "cancelled by user");
3003
+ }
2948
3004
  /** 从 cron 分支构造一条新的 ScheduleRule(丢弃 lastTriggeredAt 之外的字段)。 */
2949
3005
  function cronRule(schedule, cron, enabled, nextRunAt) {
2950
3006
  const rule = {
@@ -4153,6 +4209,11 @@ var TaskBoardHostService = class {
4153
4209
  */
4154
4210
  apply(requestId, action) {
4155
4211
  if (!this.active) throw new Error("task board is disabled");
4212
+ let cancelTargetExecutionId;
4213
+ if (action.kind === "cancel-execution") {
4214
+ const open = this.ledger.peekTasks().find((candidate) => candidate.id === action.taskId)?.executions.filter((execution) => execution.endedAt === void 0) ?? [];
4215
+ cancelTargetExecutionId = action.executionId ?? open.at(-1)?.id;
4216
+ }
4156
4217
  const before = this.ledger.summary().revision;
4157
4218
  const state = this.ledger.applyRequest(requestId, action, (doc) => this.applyAction(doc, action));
4158
4219
  if (state.revision > before) {
@@ -4160,6 +4221,9 @@ var TaskBoardHostService = class {
4160
4221
  const task = state.tasks.find((candidate) => candidate.id === action.taskId);
4161
4222
  const execution = task?.executions.at(-1);
4162
4223
  if (task !== void 0 && execution !== void 0) this.scheduleLaunch(task, execution);
4224
+ } else if (action.kind === "cancel-execution" && cancelTargetExecutionId !== void 0) {
4225
+ const execution = state.tasks.find((candidate) => candidate.id === action.taskId)?.executions.find((candidate) => candidate.id === cancelTargetExecutionId);
4226
+ if (execution?.sessionId !== void 0) this.scheduleSessionCancel(execution.sessionId);
4163
4227
  } else if (action.kind === "run-automation") this.scheduleAutomationRun(action.ruleId);
4164
4228
  } else if (action.kind === "start-split") {
4165
4229
  const job = this.splitJobs.get(action.id);
@@ -4247,6 +4311,7 @@ var TaskBoardHostService = class {
4247
4311
  case "set-schedule": return applySetSchedule(doc, action.taskId, action.patch, this.now());
4248
4312
  case "set-one-shot": return applySetOneShot(doc, action.taskId, action.patch, this.now());
4249
4313
  case "delete": return applyDeleteTask(doc, action.taskId);
4314
+ case "cancel-execution": return applyCancelExecution(doc, action.taskId, action.executionId, this.now());
4250
4315
  case "move": return applyMoveTask(doc, action.taskId, action.status, this.now(), action.order);
4251
4316
  case "reorder": return applyReorderTask(doc, action.taskId, action.status, action.order, {
4252
4317
  ...action.project !== void 0 ? { project: action.project } : {},
@@ -4275,6 +4340,18 @@ var TaskBoardHostService = class {
4275
4340
  });
4276
4341
  }
4277
4342
  /**
4343
+ * 异步停止底层会话(用户取消执行,§13.3):尽力而为——runner 未接线/
4344
+ * 不支持 cancelSession 时 no-op;RPC 失败仅记日志(账本侧 cancelled 结算
4345
+ * 已权威,不因会话停止失败而回滚)。
4346
+ */
4347
+ scheduleSessionCancel(sessionId) {
4348
+ const runner = this.runner;
4349
+ if (runner?.cancelSession === void 0) return;
4350
+ runner.cancelSession(sessionId).catch((error) => {
4351
+ console.warn(`[dsh-task-board] session cancel failed (best-effort): ${error instanceof Error ? error.message : String(error)}`);
4352
+ });
4353
+ }
4354
+ /**
4278
4355
  * launch 编排(§13.2 尾部):runner 创建会话成功 → 回填 sessionId(之后由
4279
4356
  * 轮询结算);任何失败 → 结算 failed 并保留错误文本(会话创建后的失败携带
4280
4357
  * sessionId,先回填再结算,详情页保留跳转线索)。
@@ -4765,7 +4842,7 @@ const inject = [
4765
4842
  */
4766
4843
  const NOVA_TASK_BOARD_SETTINGS_NAMESPACE = settingsNamespace("nova-task-board");
4767
4844
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
4768
- const TASK_BOARD_GUIDANCE = "本机已安装 dsh-nova-ui-task-board 插件(DSH Web GUI 的任务看板,Nova 系列):侧边栏「任务看板」入口。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护。对话候选任务流转(P3.1):用户要求「做某事」「记下来」「后续要处理」等明确任务意图时,在回复末尾输出一行结构化候选标记 `⟦task-board:propose⟧ 标题 | 描述 | prompt`(描述可为空,prompt 为执行时发送给 agent 的完整指令、可省略),前端解析后进入看板「待确认」列,经用户确认后方可执行。用户提到「任务看板 / 看板 / 定时任务 / 候选任务」时即指本插件,请据此协作。";
4845
+ const TASK_BOARD_GUIDANCE = "本机已安装 dsh-nova-ui-task-board 插件(DSH Web GUI Nova 任务看板,区别于参考实现「任务看板」):侧边栏「Nova 任务看板」入口。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护。对话候选任务流转(P3.1):用户要求「做某事」「记下来」「后续要处理」等明确任务意图时,在回复末尾输出一行结构化候选标记 `⟦task-board:propose⟧ 标题 | 描述 | prompt`(描述可为空,prompt 为执行时发送给 agent 的完整指令、可省略),前端解析后进入看板「待确认」列,经用户确认后方可执行。用户提到「任务看板 / Nova 任务看板 / 看板 / 定时任务 / 候选任务」时即指本插件(若同时装有参考实现「任务看板」,请优先使用本插件的「Nova 任务看板」),请据此协作。";
4769
4846
  const Config = z.object({
4770
4847
  announceToAgent: z.boolean().default(false),
4771
4848
  enabled: z.boolean().default(true),
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The "Nova 插件" settings section (a first-level entry in the settings nav).
3
+ *
4
+ * This is our OWN top-level settings page, distinct from the reference
5
+ * "Web UI 插件" group: it declares its own child slot (`nova.plugin.item`)
6
+ * and its own locale namespace (`nova-plugins`), so the dsh-nova-ui family
7
+ * plugins never share the reference group's slot, id, or namespace
8
+ * (AGENTS.md D2/D4 — the reference implementation `@linxin666/dsh-web-ui`
9
+ * already occupies the `web-ui.plugin` slot family and the `web-ui-plugins`
10
+ * section id, and we must not collide with it).
11
+ *
12
+ * The section body is deliberately thin: it renders the family plugin
13
+ * configuration cards contributed into `nova.plugin.item` (today that is
14
+ * `NovaTaskBoardSettingsCard`; future Nova family plugins add their own), so
15
+ * expanding the family never means editing this file.
16
+ */
17
+ import type { ReactNode } from 'react';
18
+ import type { PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
19
+ /** Full composed component props (settings.section seat + the child-slot render + the group locale). */
20
+ export type NovaPluginsSectionProps = PropsRuntime<'settings.section'> & PropsRenderSlots<'nova.plugin.item'> & PropsLocale<'nova-plugins'>;
21
+ /**
22
+ * Render the Nova plugins section content column: a heading, a lede, and the
23
+ * family plugin cards contributed into `nova.plugin.item`.
24
+ * @param props - composed slot props (close, renderSlot, t).
25
+ * @returns the section.
26
+ */
27
+ export declare function NovaPluginsSection(props: NovaPluginsSectionProps): ReactNode;
@@ -89,7 +89,14 @@ export interface BoardSnapshot {
89
89
  executionOptions: ExecutionOptionsSnapshot;
90
90
  /** 已提交、等待 Host 确认的任务 id(只做展示,不先写状态)。 */
91
91
  pendingTaskIds: readonly string[];
92
+ /** 最近一次 Host/传输错误(业务拒绝或传输失败统一进此字段展示)。 */
92
93
  transportError?: string;
94
+ /**
95
+ * 该错误是否可通过「重试连接 Host」修复:true = 传输失败(网络/超时/解析,
96
+ * 重试有意义);false = Host 业务拒绝(HTTP 4xx,重试无意义,错误条据此
97
+ * 不渲染重试按钮)。
98
+ */
99
+ transportErrorRetryable?: boolean;
93
100
  /** Host 元信息:revision + scheduler(时区)+ power。 */
94
101
  host?: {
95
102
  revision: number;
@@ -135,7 +142,7 @@ export declare class BoardController {
135
142
  private readonly uuid;
136
143
  private readonly pendingTaskIds;
137
144
  private readonly taskQueues;
138
- private transportError;
145
+ private transportFailure;
139
146
  private hostState;
140
147
  private remoteSubscribed;
141
148
  private remoteInitialization;
@@ -214,6 +221,13 @@ export declare class BoardController {
214
221
  tags?: string[];
215
222
  }): Promise<boolean>;
216
223
  deleteTask(id: string): void;
224
+ /**
225
+ * 停止运行中的执行(§11.2 cancel-execution,用户手动取消):Host 把目标执行
226
+ * 结算为 cancelled → 任务回落 todo,running 独占随即释放——随后可移动/删除/
227
+ * 归档。`executionId` 缺省 = 最后一条未结算执行;底层会话由 Host 尽力停止。
228
+ * 返回 Host 是否确认。
229
+ */
230
+ cancelExecution(id: string, executionId?: string): Promise<boolean>;
217
231
  /** 归档已结算任务(done/failed)。Host 守卫拒绝非法归档并置错误条。 */
218
232
  archiveTask(id: string): void;
219
233
  /** 恢复归档任务回原列。确认后关闭详情(任务已离开归档视图)。 */
@@ -257,6 +271,8 @@ export declare class BoardController {
257
271
  private performRemote;
258
272
  private initializeRemote;
259
273
  private doInitializeRemote;
274
+ /** 幂等建立 SSE 订阅(bootstrap 失败也要订阅,见 doInitializeRemote 注释)。 */
275
+ private ensureRemoteSubscription;
260
276
  /**
261
277
  * SSE 帧处理(§11.1):revision 与已应用的一致 → 就地更新 scheduler/power
262
278
  * (不动任务列表,memo 边界保持);否则(帧不完整/新 revision/同步信号)重拉
@@ -21,6 +21,15 @@ import type { TaskRecord } from '../core/model.ts';
21
21
  import { type TaskBoardAction, type TaskBoardEventPayload, type TaskBoardSnapshot } from '../protocol.ts';
22
22
  /** 单次 Host 请求超时(§17 性能边界;超时按传输错误暴露,可重试)。 */
23
23
  export declare const REQUEST_TIMEOUT_MS = 15000;
24
+ /**
25
+ * Host 业务拒绝(HTTP 4xx,§11.1):与传输层错误区分——业务拒绝由操作修正,
26
+ * 重试无意义;错误条据此**不显示「重试连接 Host」**,只对传输失败(网络/
27
+ * 超时/解析)保留重试入口(board-controller 按 instanceof 判定 retryable)。
28
+ */
29
+ export declare class TaskBoardRequestError extends Error {
30
+ readonly status: number;
31
+ constructor(message: string, status: number);
32
+ }
24
33
  /** 浏览器存储的读写面(localStorage;测试注入 fake)。 */
25
34
  export interface MarkerStorage {
26
35
  getItem(key: string): string | null;
@@ -1,27 +1,30 @@
1
1
  import type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client';
2
2
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
3
- import { type NovaTaskBoardKey } from './locales.ts';
3
+ import { type NovaPluginsKey, type NovaTaskBoardKey } from './locales.ts';
4
4
  declare module '@deepseek-ai/dsh-client-ui-slots' {
5
5
  interface LocaleNamespaceMap {
6
6
  /** Nova task-board surface copy. */
7
7
  'nova-task-board': NovaTaskBoardKey;
8
+ /** Locale copy of the "Nova 插件" settings section heading/lede. */
9
+ 'nova-plugins': NovaPluginsKey;
8
10
  }
9
11
  interface SlotMap {
10
12
  /**
11
- * The child slot the Web UI plugin group declares; this card registers
12
- * into the group instead of the top-level `settings.section` list.
13
- * Spelled here with the same shape so this package can register without
14
- * depending on the sibling UI package.
13
+ * The child slot the "Nova 插件" section declares. The dsh-nova-ui family
14
+ * plugins contribute their configuration cards here NOT into the
15
+ * reference `web-ui.plugin.item` child of the "Web UI 插件" group, which
16
+ * `@linxin666/dsh-web-ui` owns (AGENTS.md D4). Spelled here with the same
17
+ * shape so this package can register without depending on the group plugin.
15
18
  */
16
- 'web-ui.plugin.item': {
19
+ 'nova.plugin.item': {
17
20
  kind: 'list';
18
21
  scope: 'root';
19
- owner: WebUiPluginItemOwnerProps;
22
+ owner: NovaPluginItemOwnerProps;
20
23
  };
21
24
  }
22
25
  }
23
26
  /** Owner share of a plugin card (the section supplies nothing). */
24
- export interface WebUiPluginItemOwnerProps {
27
+ export interface NovaPluginItemOwnerProps {
25
28
  /** Marker field: card owner props are intentionally empty. */
26
29
  children?: never;
27
30
  }
@@ -50,6 +50,7 @@ export declare const zh: {
50
50
  'detail.noExecution': string;
51
51
  'detail.run': string;
52
52
  'detail.rerun': string;
53
+ 'detail.stop': string;
53
54
  'detail.delete': string;
54
55
  'detail.archive': string;
55
56
  'detail.restore': string;
@@ -65,6 +66,9 @@ export declare const zh: {
65
66
  'delete.confirm': string;
66
67
  'delete.ok': string;
67
68
  'delete.cancel': string;
69
+ 'stop.title': string;
70
+ 'stop.confirm': string;
71
+ 'stop.ok': string;
68
72
  'status.move.backlog': string;
69
73
  'status.move.todo': string;
70
74
  'time.justNow': string;
@@ -200,3 +204,17 @@ export type NovaTaskBoardKey = keyof typeof zh;
200
204
  export declare function dictionary(): Record<NovaTaskBoardKey, string>;
201
205
  /** Translate a key with optional {name} template params. */
202
206
  export declare function t(key: NovaTaskBoardKey, params?: Record<string, string>): string;
207
+ /**
208
+ * Locale dictionary for the "Nova 插件" settings section (the first-level nav
209
+ * entry that hosts the dsh-nova-ui family plugin cards). Kept as its own
210
+ * namespace (`nova-plugins`) so the section copy never collides with the
211
+ * task-board card's `nova-task-board` namespace (AGENTS.md D4). Key set 以 zh 为准。
212
+ */
213
+ export declare const groupZh: {
214
+ title: string;
215
+ description: string;
216
+ };
217
+ /** en dictionary, complete against the groupZh key set. */
218
+ export declare const groupEn: Record<keyof typeof groupZh, string>;
219
+ /** The "Nova 插件" section dictionary key union. */
220
+ export type NovaPluginsKey = keyof typeof groupZh;
@@ -64,6 +64,11 @@
64
64
  * proposed 守卫)、不可作为拖拽来源(T009 已含);编辑(update)允许
65
65
  * (§10.2「只读(除编辑/确认/拒绝)」)。
66
66
  *
67
+ * 用户取消执行(running 解锁):`applyCancelExecution`(§11.2 cancel-execution,
68
+ * 用户手动停止)——把目标未结算执行结算为 cancelled(endedAt/result/error 落账),
69
+ * 任务按 settleStatusOf 回落 todo,running 独占随即释放(可移动/删除/归档);
70
+ * 底层会话的停止由 Host 尽力而为(runner.cancelSession),本层只做账本侧权威结算。
71
+ *
67
72
  * T012 需求拆分(§11.2/§12.9/§14.5,P3.2):
68
73
  * - `applyProposeBatch`:propose-batch(批量创建候选,需求拆分产物)——
69
74
  * items 逐条沿用 propose 的清洗(标题/描述/prompt 控制字符 + 长度封顶,
@@ -285,6 +290,19 @@ export declare function settleStatusOf(result: ExecutionResult, current: TaskSta
285
290
  * 守卫:执行存在;已结算执行为 no-op(返回 false,不 bump revision)。
286
291
  */
287
292
  export declare function applySettleExecution(doc: LedgerDocument, taskId: string, executionId: string, result: ExecutionResult, now: number, error?: string): boolean;
293
+ /**
294
+ * cancel-execution(§11.2,用户手动停止执行):把一条未结算执行结算为
295
+ * cancelled(endedAt=now、result=cancelled、error 注明用户取消),任务按
296
+ * settleStatusOf 回落(cancelled → todo)——running 独占随即释放,任务恢复
297
+ * 可移动/可删除/可归档。这是 running 任务「停不下来」的唯一解锁路径:即使
298
+ * 底层会话已停滞(无 turn/end、会话列表仍在),账本侧结算仍然权威生效。
299
+ *
300
+ * 守卫:任务存在;无未结算执行 → 抛错('task has no running execution');
301
+ * `executionId` 缺省取最后一条未结算执行,提供时须是该任务的未结算执行
302
+ * (已结算执行不可作为取消目标,否则 'execution not found')。
303
+ * 底层会话的停止由 Host 尽力而为(runner.cancelSession),本层不触碰会话。
304
+ */
305
+ export declare function applyCancelExecution(doc: LedgerDocument, taskId: string, executionId: string | undefined, now: number): boolean;
288
306
  /** set-schedule 补丁(§11.2):只允许 enabled/cron 两个浏览器可写字段。 */
289
307
  export interface SetSchedulePatch {
290
308
  enabled?: boolean;
@@ -120,6 +120,11 @@ export interface ExecutionApi {
120
120
  events: HistoryEntry[];
121
121
  hasMore: boolean;
122
122
  }>>;
123
+ cancel(request: RpcRequest<{
124
+ sessionId: SessionId;
125
+ }>): Promise<RpcResponse<{
126
+ accepted: true;
127
+ }>>;
123
128
  };
124
129
  }
125
130
  /** 会话创建后的启动失败:仍标识会话(服务层先回填 sessionId 再结算 failed)。 */
@@ -167,6 +172,12 @@ export declare class HostExecutionRunner {
167
172
  * 实现);缺省时自行拉取。
168
173
  */
169
174
  inspect(sessionId: string, startedAt: number, sessions?: readonly ExecutionSessionRow[]): Promise<ExecutionInspection>;
175
+ /**
176
+ * 停止底层会话(§13.3 用户取消执行):尽力而为——RPC 失败/被拒/会话不存在
177
+ * 都只记日志,不抛给控制面(账本侧 cancelled 结算已权威,本调用只是避免
178
+ * 孤儿 agent 继续空转/烧额度)。
179
+ */
180
+ cancelSession(sessionId: string): Promise<void>;
170
181
  /**
171
182
  * 读取会话 transcript 全文(T012/B8:拆分会话结果解析用)。翻页读取历史事件,
172
183
  * 拼接全部 assistant 消息文本(顺序拼接、换行分隔;总长封顶
@@ -94,6 +94,12 @@ export interface TaskBoardExecutionRunner {
94
94
  * 历史不可读 → undefined。拆分会话是独立专用会话,全文即拆分结果。
95
95
  */
96
96
  readTranscriptText(sessionId: string): Promise<string | undefined>;
97
+ /**
98
+ * 停止底层会话(用户取消执行,§13.3):尽力而为——缺省/失败不影响账本侧
99
+ * cancelled 结算(cancel-execution 的 ledger 落账不依赖它)。可缺省(测试
100
+ * fake 无需实现)。
101
+ */
102
+ cancelSession?(sessionId: string): Promise<void>;
97
103
  }
98
104
  /**
99
105
  * 需求拆分作业(T012/B8,§12.9/§14.5):一次 `start-split` 对应一个独立拆分
@@ -176,6 +182,12 @@ export declare class TaskBoardHostService {
176
182
  private applyAction;
177
183
  /** 异步调度 launch(fire-and-forget;结算失败仅记录日志,不中断控制面)。 */
178
184
  private scheduleLaunch;
185
+ /**
186
+ * 异步停止底层会话(用户取消执行,§13.3):尽力而为——runner 未接线/
187
+ * 不支持 cancelSession 时 no-op;RPC 失败仅记日志(账本侧 cancelled 结算
188
+ * 已权威,不因会话停止失败而回滚)。
189
+ */
190
+ private scheduleSessionCancel;
179
191
  /**
180
192
  * launch 编排(§13.2 尾部):runner 创建会话成功 → 回填 sessionId(之后由
181
193
  * 轮询结算);任何失败 → 结算 failed 并保留错误文本(会话创建后的失败携带
@@ -31,7 +31,7 @@ export declare const inject: readonly ["systemPrompt", "webServer", "apiProxy",
31
31
  */
32
32
  export declare const NOVA_TASK_BOARD_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
33
33
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
34
- export declare const TASK_BOARD_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-nova-ui-task-board \u63D2\u4EF6\uFF08DSH Web GUI \u7684\u4EFB\u52A1\u770B\u677F\uFF0CNova \u7CFB\u5217\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u4EFB\u52A1\u770B\u677F\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u591A\u5217\u770B\u677F\u7BA1\u7406\u4EFB\u52A1\uFF1BHost \u6743\u5A01\u8D26\u672C\uFF1B\u5173\u95ED\u6D4F\u89C8\u5668\u540E\u4ECD\u7531 Host \u6267\u884C\u548C\u7ED3\u7B97\uFF1B\u4EFB\u52A1\u53EF\u9489\u4F4F\u5DE5\u4F5C\u533A\u3001agent \u9884\u8BBE\u548C\u6743\u9650\uFF1B\u652F\u6301 Host \u672C\u5730\u65F6\u533A\u7684 5 \u6BB5 cron\uFF0C\u9519\u8FC7\u7684\u89E6\u53D1\u70B9\u4E0D\u8865\u8DD1\uFF1B\u53EF\u9009\u4E14\u9ED8\u8BA4\u5173\u95ED\u7684\u7A7A\u95F2\u7CFB\u7EDF\u7761\u7720\u4FDD\u62A4\u3002\u5BF9\u8BDD\u5019\u9009\u4EFB\u52A1\u6D41\u8F6C\uFF08P3.1\uFF09\uFF1A\u7528\u6237\u8981\u6C42\u300C\u505A\u67D0\u4E8B\u300D\u300C\u8BB0\u4E0B\u6765\u300D\u300C\u540E\u7EED\u8981\u5904\u7406\u300D\u7B49\u660E\u786E\u4EFB\u52A1\u610F\u56FE\u65F6\uFF0C\u5728\u56DE\u590D\u672B\u5C3E\u8F93\u51FA\u4E00\u884C\u7ED3\u6784\u5316\u5019\u9009\u6807\u8BB0 `\u27E6task-board:propose\u27E7 \u6807\u9898 | \u63CF\u8FF0 | prompt`\uFF08\u63CF\u8FF0\u53EF\u4E3A\u7A7A\uFF0Cprompt \u4E3A\u6267\u884C\u65F6\u53D1\u9001\u7ED9 agent \u7684\u5B8C\u6574\u6307\u4EE4\u3001\u53EF\u7701\u7565\uFF09\uFF0C\u524D\u7AEF\u89E3\u6790\u540E\u8FDB\u5165\u770B\u677F\u300C\u5F85\u786E\u8BA4\u300D\u5217\uFF0C\u7ECF\u7528\u6237\u786E\u8BA4\u540E\u65B9\u53EF\u6267\u884C\u3002\u7528\u6237\u63D0\u5230\u300C\u4EFB\u52A1\u770B\u677F / \u770B\u677F / \u5B9A\u65F6\u4EFB\u52A1 / \u5019\u9009\u4EFB\u52A1\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
34
+ export declare const TASK_BOARD_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-nova-ui-task-board \u63D2\u4EF6\uFF08DSH Web GUI \u7684 Nova \u4EFB\u52A1\u770B\u677F\uFF0C\u533A\u522B\u4E8E\u53C2\u8003\u5B9E\u73B0\u300C\u4EFB\u52A1\u770B\u677F\u300D\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300CNova \u4EFB\u52A1\u770B\u677F\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u591A\u5217\u770B\u677F\u7BA1\u7406\u4EFB\u52A1\uFF1BHost \u6743\u5A01\u8D26\u672C\uFF1B\u5173\u95ED\u6D4F\u89C8\u5668\u540E\u4ECD\u7531 Host \u6267\u884C\u548C\u7ED3\u7B97\uFF1B\u4EFB\u52A1\u53EF\u9489\u4F4F\u5DE5\u4F5C\u533A\u3001agent \u9884\u8BBE\u548C\u6743\u9650\uFF1B\u652F\u6301 Host \u672C\u5730\u65F6\u533A\u7684 5 \u6BB5 cron\uFF0C\u9519\u8FC7\u7684\u89E6\u53D1\u70B9\u4E0D\u8865\u8DD1\uFF1B\u53EF\u9009\u4E14\u9ED8\u8BA4\u5173\u95ED\u7684\u7A7A\u95F2\u7CFB\u7EDF\u7761\u7720\u4FDD\u62A4\u3002\u5BF9\u8BDD\u5019\u9009\u4EFB\u52A1\u6D41\u8F6C\uFF08P3.1\uFF09\uFF1A\u7528\u6237\u8981\u6C42\u300C\u505A\u67D0\u4E8B\u300D\u300C\u8BB0\u4E0B\u6765\u300D\u300C\u540E\u7EED\u8981\u5904\u7406\u300D\u7B49\u660E\u786E\u4EFB\u52A1\u610F\u56FE\u65F6\uFF0C\u5728\u56DE\u590D\u672B\u5C3E\u8F93\u51FA\u4E00\u884C\u7ED3\u6784\u5316\u5019\u9009\u6807\u8BB0 `\u27E6task-board:propose\u27E7 \u6807\u9898 | \u63CF\u8FF0 | prompt`\uFF08\u63CF\u8FF0\u53EF\u4E3A\u7A7A\uFF0Cprompt \u4E3A\u6267\u884C\u65F6\u53D1\u9001\u7ED9 agent \u7684\u5B8C\u6574\u6307\u4EE4\u3001\u53EF\u7701\u7565\uFF09\uFF0C\u524D\u7AEF\u89E3\u6790\u540E\u8FDB\u5165\u770B\u677F\u300C\u5F85\u786E\u8BA4\u300D\u5217\uFF0C\u7ECF\u7528\u6237\u786E\u8BA4\u540E\u65B9\u53EF\u6267\u884C\u3002\u7528\u6237\u63D0\u5230\u300C\u4EFB\u52A1\u770B\u677F / Nova \u4EFB\u52A1\u770B\u677F / \u770B\u677F / \u5B9A\u65F6\u4EFB\u52A1 / \u5019\u9009\u4EFB\u52A1\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF08\u82E5\u540C\u65F6\u88C5\u6709\u53C2\u8003\u5B9E\u73B0\u300C\u4EFB\u52A1\u770B\u677F\u300D\uFF0C\u8BF7\u4F18\u5148\u4F7F\u7528\u672C\u63D2\u4EF6\u7684\u300CNova \u4EFB\u52A1\u770B\u677F\u300D\uFF09\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
35
35
  /** Plugin config, validated by the same-named schemastery schema. */
36
36
  export interface Config {
37
37
  /**
@@ -133,6 +133,10 @@ export type TaskBoardAction = {
133
133
  } | {
134
134
  kind: 'rerun';
135
135
  taskId: string;
136
+ } | {
137
+ kind: 'cancel-execution';
138
+ taskId: string;
139
+ executionId?: string;
136
140
  } | {
137
141
  kind: 'propose';
138
142
  id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@william2000/dsh-nova-ui-task-board",
3
3
  "description": "Host-authoritative task board for the DSH Web GUI with real session execution, Host cron/one-shot scheduling, sorting/grouping/drag-drop, tag & project filtering; mounted without DSH source changes. Nova 系列核心功能包(任务看板插件本体:Host 服务 + 浏览器 UI)。",
4
- "version": "0.1.1",
4
+ "version": "0.2.1",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=20"