@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/client.js +1519 -1382
- package/lib/client.js.map +1 -1
- package/lib/index.js +78 -1
- package/lib/types/client/NovaPluginsSection.d.ts +27 -0
- package/lib/types/client/board-controller.d.ts +17 -1
- package/lib/types/client/host-api.d.ts +9 -0
- package/lib/types/client/index.d.ts +11 -8
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/core/transitions.d.ts +18 -0
- package/lib/types/host-runner.d.ts +11 -0
- package/lib/types/host-service.d.ts +12 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/protocol.d.ts +4 -0
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
-
'
|
|
19
|
+
'nova.plugin.item': {
|
|
17
20
|
kind: 'list';
|
|
18
21
|
scope: 'root';
|
|
19
|
-
owner:
|
|
22
|
+
owner: NovaPluginItemOwnerProps;
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
/** Owner share of a plugin card (the section supplies nothing). */
|
|
24
|
-
export interface
|
|
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 并保留错误文本(会话创建后的失败携带
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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\
|
|
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
|
/**
|
package/lib/types/protocol.d.ts
CHANGED
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.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20"
|