@linxin666/dsh-client-ui-task-board 0.2.4 → 0.2.5

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: e9bcccc740d89fdd20a5142efa96dfe79ec842ab
6
- README.zh.md: bd1b15821bda1577565044c8f9a8e34c1aadfb82
5
+ README.md: 359b35160b1cf7a8c697152b4e3fc60193ed3599
6
+ README.zh.md: 535ac2b5c8b4c6fdbff61c08b9cadae3b5679cc7
package/README.md CHANGED
@@ -18,7 +18,7 @@ A hot-pluggable DeepSeek Harness (DSH) Web GUI plugin with a Host-authoritative
18
18
  - **Deterministic recovery**: a running execution with a recorded session is observed after restart; an interrupted start without a session id is cancelled and is not resent.
19
19
  - **Live synchronization**: mutations return a full revisioned snapshot; SSE announces revision, scheduler, and power changes, while reconnect and page visibility recovery fetch a full snapshot.
20
20
  - **Optional idle-sleep protection**: off by default; when enabled it covers every running DSH session, enabled non-archived task-board schedules, and unknown session state.
21
- - **System-prompt injection**: the Host registers a `plugin:task-board` section (order 200) through `SystemPrompt.section`, and the task-board settings can disable the announcement without disabling the board.
21
+ - **System-prompt injection**: the Host registers a `plugin:task-board` section (order 200) through `SystemPrompt.section`, and the task-board settings can disable the announcement without disabling the board. The guidance also reminds agents to close any visible `todo_write` plan before the final answer.
22
22
 
23
23
  ## Architecture and protocol
24
24
 
package/README.zh.md CHANGED
@@ -18,7 +18,7 @@
18
18
  - **确定性恢复**:已有 session id 的 running execution 在重启后继续观察;没有 session id 的启动中断会取消且不会重发。
19
19
  - **实时同步**:变更返回完整 revision snapshot;SSE 只提示 revision、scheduler 与 power 变化,重连和页面恢复可见时重新拉完整 snapshot。
20
20
  - **可选空闲睡眠保护**:默认关闭;开启后覆盖全部运行中的 DSH 会话、已启用且未归档的任务计划和未知会话状态。
21
- - **系统提示词注入**:Host 通过 `SystemPrompt.section` 注册 order 200 的 `plugin:task-board` 段;任务看板设置可单独关闭声明而不关闭看板。
21
+ - **系统提示词注入**:Host 通过 `SystemPrompt.section` 注册 order 200 的 `plugin:task-board` 段;任务看板设置可单独关闭声明而不关闭看板。该提示也会提醒 agent 在最终回复前收尾可见的 `todo_write` 计划列表。
22
22
 
23
23
  ## 架构与协议
24
24
 
package/lib/index.js CHANGED
@@ -1361,8 +1361,10 @@ function isErrorTurnEnd(data) {
1361
1361
  }
1362
1362
  var HostExecutionRunner = class {
1363
1363
  api;
1364
- constructor(api) {
1364
+ commands;
1365
+ constructor(api, commands) {
1365
1366
  this.api = api;
1367
+ this.commands = commands;
1366
1368
  }
1367
1369
  async launch(task) {
1368
1370
  if (task.workspaceId !== void 0) {
@@ -1390,16 +1392,10 @@ var HostExecutionRunner = class {
1390
1392
  }));
1391
1393
  if (!renamed.result.ok) throw failure(renamed.result.error);
1392
1394
  if (task.permission !== void 0) {
1393
- const command = await this.api.sessions.prompt(request({
1394
- sessionId,
1395
- mode: "queue",
1396
- content: [{
1397
- type: "text",
1398
- text: `/permission ${task.permission}`
1399
- }]
1400
- }));
1401
- if (!command.result.ok) throw failure(command.result.error);
1402
- if (command.result.value.command?.kind !== "success") throw new Error("permission command was not acknowledged");
1395
+ if (this.commands === void 0) throw new Error("permission command dispatcher is unavailable");
1396
+ const command = await this.commands.execute(sessionId, `/permission ${task.permission}`, AbortSignal.timeout(3e4));
1397
+ if (command === void 0) throw new Error("permission command was not acknowledged");
1398
+ if (command.kind !== "success") throw new Error(command.text ?? "permission command failed");
1403
1399
  }
1404
1400
  const prompt = await this.api.sessions.prompt(request({
1405
1401
  sessionId,
@@ -1805,7 +1801,7 @@ var TaskBoardHostService = class {
1805
1801
  now;
1806
1802
  constructor(api, options = {}) {
1807
1803
  this.ledger = options.ledger ?? new HostTaskLedger();
1808
- this.runner = new HostExecutionRunner(api);
1804
+ this.runner = new HostExecutionRunner(api, options.commandDispatcher);
1809
1805
  this.power = options.power ?? new PowerInhibitor();
1810
1806
  this.now = options.now ?? Date.now;
1811
1807
  this.ledger.subscribe(() => {
@@ -2277,10 +2273,12 @@ const DEFAULT_PROXY_TOKEN_ENV = "DSH_TASK_BOARD_PROXY_TOKEN";
2277
2273
  const inject = [
2278
2274
  "systemPrompt",
2279
2275
  "apiProxy",
2280
- "webServer"
2276
+ "webServer",
2277
+ "agents",
2278
+ "commands"
2281
2279
  ];
2282
2280
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
2283
- const TASK_BOARD_GUIDANCE = "本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。";
2281
+ const TASK_BOARD_GUIDANCE = "本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。若你同时用 todo_write 维护会话顶部的可见计划列表,最终回复前必须再次调用 todo_write 收尾:没有剩余工作时不要保留 in_progress,已完成的最后一步要标为 completed。";
2284
2282
  /**
2285
2283
  * Settings namespace of the board's announcement capability — the section the
2286
2284
  * web settings surface edits. Spelled here rather than imported: the browser
@@ -2319,7 +2317,11 @@ const DEFAULT_ANNOUNCE = true;
2319
2317
  */
2320
2318
  const apply = mountOnce("@linxin666/dsh-client-ui-task-board", applyImpl);
2321
2319
  function applyImpl(ctx, config) {
2322
- const host = new TaskBoardHostService(ctx.apiProxy);
2320
+ const host = new TaskBoardHostService(ctx.apiProxy, { commandDispatcher: { async execute(sessionId, line, signal) {
2321
+ const agent = ctx.agents.get(sessionId);
2322
+ if (agent === void 0) throw new Error(`execution session ${sessionId} is not available`);
2323
+ return (await ctx.commands.execute(agent, line, [], signal))?.result;
2324
+ } } });
2323
2325
  host.setConfiguration(config?.enabled ?? true, config?.preventIdleSleep ?? false);
2324
2326
  host.start();
2325
2327
  ctx.effect(() => {
@@ -1,9 +1,16 @@
1
1
  import type { ApiProxy } from '@deepseek-ai/dsh-host-apiproxy';
2
+ import type { CommandResult } from '@deepseek-ai/dsh-commands/types';
2
3
  import type { TaskRecord } from './core/tasks.ts';
3
4
  /** One session-list row, extracted from the sessions.list RPC result. */
4
5
  export type SessionSummary = Extract<Awaited<ReturnType<ApiProxy['sessions']['list']>>['result'], {
5
6
  ok: true;
6
7
  }>['value']['items'][number];
8
+ type ExecutionSessionId = Extract<Awaited<ReturnType<ApiProxy['sessions']['create']>>['result'], {
9
+ ok: true;
10
+ }>['value']['sessionId'];
11
+ export interface SessionCommandDispatcher {
12
+ execute(sessionId: ExecutionSessionId, line: string, signal: AbortSignal): Promise<CommandResult | undefined>;
13
+ }
7
14
  export type ExecutionInspection = {
8
15
  outcome: 'pending';
9
16
  } | {
@@ -22,7 +29,8 @@ export declare class SessionLaunchError extends Error {
22
29
  }
23
30
  export declare class HostExecutionRunner {
24
31
  private readonly api;
25
- constructor(api: ApiProxy);
32
+ private readonly commands?;
33
+ constructor(api: ApiProxy, commands?: SessionCommandDispatcher | undefined);
26
34
  launch(task: TaskRecord): Promise<string>;
27
35
  listRunning(): Promise<{
28
36
  known: true;
@@ -39,3 +47,4 @@ export declare class HostExecutionRunner {
39
47
  */
40
48
  inspect(sessionId: string, startedAt?: number, sessions?: readonly SessionSummary[]): Promise<ExecutionInspection>;
41
49
  }
50
+ export {};
@@ -1,6 +1,6 @@
1
1
  import type { ApiProxy } from '@deepseek-ai/dsh-host-apiproxy';
2
2
  import { HostTaskLedger } from './host-ledger.ts';
3
- import { HostExecutionRunner } from './host-runner.ts';
3
+ import { HostExecutionRunner, type SessionCommandDispatcher } from './host-runner.ts';
4
4
  import { PowerInhibitor } from './power-inhibitor.ts';
5
5
  import type { TaskBoardAction, TaskBoardEventPayload, TaskBoardSnapshot } from './protocol.ts';
6
6
  export declare class TaskBoardHostService {
@@ -21,6 +21,7 @@ export declare class TaskBoardHostService {
21
21
  ledger?: HostTaskLedger;
22
22
  power?: PowerInhibitor;
23
23
  now?: () => number;
24
+ commandDispatcher?: SessionCommandDispatcher;
24
25
  });
25
26
  start(): void;
26
27
  setConfiguration(active: boolean, preventIdleSleep: boolean): void;
@@ -11,7 +11,7 @@ import z from 'schemastery';
11
11
  export declare const DEFAULT_PROXY_TOKEN_ENV = "DSH_TASK_BOARD_PROXY_TOKEN";
12
12
  export declare const inject: string[];
13
13
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
14
- export declare const TASK_BOARD_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-task-board \u63D2\u4EF6\uFF08DSH Web GUI \u7684\u4EFB\u52A1\u770B\u677F\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u4EFB\u52A1\u770B\u677F\u300D\u5165\u53E3\uFF1B\u5728 dsh-web-ui \u63D2\u4EF6\u5168\u5BB6\u6876\u4ED3\u5E93\uFF08packages/dsh-task-board\uFF09\u7EDF\u4E00\u7EF4\u62A4\uFF0C\u7ECF\u805A\u5408\u5305 web-ui-all \u4E00\u952E\u5B89\u88C5\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\u5141\u8BB8\u5C4F\u5E55\u7184\u706D\uFF0C\u4F46\u4E0D\u627F\u8BFA\u62E6\u622A\u5408\u76D6\u3001\u624B\u52A8\u7761\u7720\u3001\u4F11\u7720\u3001\u5173\u673A\u6216\u5524\u9192\u5DF2\u7761\u7720\u673A\u5668\u3002\u6267\u884C\u6D88\u8017 API \u989D\u5EA6\u3002\u7528\u6237\u63D0\u5230\u300C\u4EFB\u52A1\u770B\u677F / \u770B\u677F / \u5B9A\u65F6\u4EFB\u52A1\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
14
+ export declare const TASK_BOARD_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-task-board \u63D2\u4EF6\uFF08DSH Web GUI \u7684\u4EFB\u52A1\u770B\u677F\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u4EFB\u52A1\u770B\u677F\u300D\u5165\u53E3\uFF1B\u5728 dsh-web-ui \u63D2\u4EF6\u5168\u5BB6\u6876\u4ED3\u5E93\uFF08packages/dsh-task-board\uFF09\u7EDF\u4E00\u7EF4\u62A4\uFF0C\u7ECF\u805A\u5408\u5305 web-ui-all \u4E00\u952E\u5B89\u88C5\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\u5141\u8BB8\u5C4F\u5E55\u7184\u706D\uFF0C\u4F46\u4E0D\u627F\u8BFA\u62E6\u622A\u5408\u76D6\u3001\u624B\u52A8\u7761\u7720\u3001\u4F11\u7720\u3001\u5173\u673A\u6216\u5524\u9192\u5DF2\u7761\u7720\u673A\u5668\u3002\u6267\u884C\u6D88\u8017 API \u989D\u5EA6\u3002\u7528\u6237\u63D0\u5230\u300C\u4EFB\u52A1\u770B\u677F / \u770B\u677F / \u5B9A\u65F6\u4EFB\u52A1\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002\u82E5\u4F60\u540C\u65F6\u7528 todo_write \u7EF4\u62A4\u4F1A\u8BDD\u9876\u90E8\u7684\u53EF\u89C1\u8BA1\u5212\u5217\u8868\uFF0C\u6700\u7EC8\u56DE\u590D\u524D\u5FC5\u987B\u518D\u6B21\u8C03\u7528 todo_write \u6536\u5C3E\uFF1A\u6CA1\u6709\u5269\u4F59\u5DE5\u4F5C\u65F6\u4E0D\u8981\u4FDD\u7559 in_progress\uFF0C\u5DF2\u5B8C\u6210\u7684\u6700\u540E\u4E00\u6B65\u8981\u6807\u4E3A completed\u3002";
15
15
  /**
16
16
  * Settings namespace of the board's announcement capability — the section the
17
17
  * web settings surface edits. Spelled here rather than imported: the browser
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-client-ui-task-board",
3
3
  "description": "Host-authoritative task board for the DSH Web GUI with real session execution, Host cron scheduling, and optional cross-platform idle-sleep protection; mounted without DSH source changes.",
4
- "version": "0.2.4",
4
+ "version": "0.2.5",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -35,6 +35,8 @@
35
35
  }
36
36
  },
37
37
  "peerDependencies": {
38
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
39
+ "@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
38
40
  "react": "^18.2.0"
39
41
  },
40
42
  "dependencies": {
@@ -42,11 +44,13 @@
42
44
  },
43
45
  "devDependencies": {
44
46
  "@deepseek-ai/cordis": "^4.0.1",
47
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
45
48
  "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
46
49
  "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
47
50
  "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
48
51
  "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
49
52
  "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
53
+ "@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
50
54
  "@deepseek-ai/dsh-host-apiproxy": "^0.1.0-rc.8",
51
55
  "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
52
56
  "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
@@ -1,4 +1,5 @@
1
1
  import type { ApiProxy, RpcId } from '@deepseek-ai/dsh-host-apiproxy'
2
+ import type { CommandResult } from '@deepseek-ai/dsh-commands/types'
2
3
  import type { TaskRecord } from './core/tasks.ts'
3
4
 
4
5
  function request<T>(payload: T) {
@@ -15,6 +16,19 @@ export type SessionSummary = Extract<
15
16
  { ok: true }
16
17
  >['value']['items'][number]
17
18
 
19
+ type ExecutionSessionId = Extract<
20
+ Awaited<ReturnType<ApiProxy['sessions']['create']>>['result'],
21
+ { ok: true }
22
+ >['value']['sessionId']
23
+
24
+ export interface SessionCommandDispatcher {
25
+ execute(
26
+ sessionId: ExecutionSessionId,
27
+ line: string,
28
+ signal: AbortSignal,
29
+ ): Promise<CommandResult | undefined>
30
+ }
31
+
18
32
  export type ExecutionInspection =
19
33
  | { outcome: 'pending' }
20
34
  | { outcome: 'succeeded' }
@@ -36,7 +50,10 @@ function isErrorTurnEnd(data: unknown): boolean {
36
50
  }
37
51
 
38
52
  export class HostExecutionRunner {
39
- constructor(private readonly api: ApiProxy) {}
53
+ constructor(
54
+ private readonly api: ApiProxy,
55
+ private readonly commands?: SessionCommandDispatcher,
56
+ ) {}
40
57
 
41
58
  async launch(task: TaskRecord): Promise<string> {
42
59
  if (task.workspaceId !== undefined) {
@@ -63,13 +80,10 @@ export class HostExecutionRunner {
63
80
  const renamed = await this.api.sessions.rename(request({ sessionId, title: task.title }))
64
81
  if (!renamed.result.ok) throw failure(renamed.result.error)
65
82
  if (task.permission !== undefined) {
66
- const command = await this.api.sessions.prompt(request({
67
- sessionId,
68
- mode: 'queue' as const,
69
- content: [{ type: 'text' as const, text: `/permission ${task.permission}` }],
70
- }))
71
- if (!command.result.ok) throw failure(command.result.error)
72
- if (command.result.value.command?.kind !== 'success') throw new Error('permission command was not acknowledged')
83
+ if (this.commands === undefined) throw new Error('permission command dispatcher is unavailable')
84
+ const command = await this.commands.execute(sessionId, `/permission ${task.permission}`, AbortSignal.timeout(30_000))
85
+ if (command === undefined) throw new Error('permission command was not acknowledged')
86
+ if (command.kind !== 'success') throw new Error(command.text ?? 'permission command failed')
73
87
  }
74
88
  const prompt = await this.api.sessions.prompt(request({
75
89
  sessionId,
@@ -2,7 +2,7 @@ import type { ApiProxy } from '@deepseek-ai/dsh-host-apiproxy'
2
2
  import { nextRunAtMs } from './core/schedule.ts'
3
3
  import type { TaskRecord } from './core/tasks.ts'
4
4
  import { HostTaskLedger, type OpenedRun } from './host-ledger.ts'
5
- import { HostExecutionRunner, SessionLaunchError, type SessionSummary } from './host-runner.ts'
5
+ import { HostExecutionRunner, SessionLaunchError, type SessionCommandDispatcher, type SessionSummary } from './host-runner.ts'
6
6
  import { PowerInhibitor } from './power-inhibitor.ts'
7
7
  import type { TaskBoardAction, TaskBoardEventPayload, TaskBoardSnapshot } from './protocol.ts'
8
8
 
@@ -25,9 +25,14 @@ export class TaskBoardHostService {
25
25
  private lastPowerJson = ''
26
26
  private readonly now: () => number
27
27
 
28
- constructor(api: ApiProxy, options: { ledger?: HostTaskLedger; power?: PowerInhibitor; now?: () => number } = {}) {
28
+ constructor(api: ApiProxy, options: {
29
+ ledger?: HostTaskLedger
30
+ power?: PowerInhibitor
31
+ now?: () => number
32
+ commandDispatcher?: SessionCommandDispatcher
33
+ } = {}) {
29
34
  this.ledger = options.ledger ?? new HostTaskLedger()
30
- this.runner = new HostExecutionRunner(api)
35
+ this.runner = new HostExecutionRunner(api, options.commandDispatcher)
31
36
  this.power = options.power ?? new PowerInhibitor()
32
37
  this.now = options.now ?? Date.now
33
38
  this.ledger.subscribe(() => {
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@
7
7
  */
8
8
 
9
9
  import type { Context } from '@deepseek-ai/cordis'
10
+ import type {} from '@deepseek-ai/dsh-agent'
11
+ import type {} from '@deepseek-ai/dsh-commands'
10
12
  import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
11
13
  import z from 'schemastery'
12
14
  import type {} from '@deepseek-ai/dsh-system-prompt'
@@ -22,10 +24,10 @@ const SECTION_ORDER = 200
22
24
  /** Default environment variable holding the authenticated proxy token. */
23
25
  export const DEFAULT_PROXY_TOKEN_ENV = 'DSH_TASK_BOARD_PROXY_TOKEN'
24
26
 
25
- export const inject = ['systemPrompt', 'apiProxy', 'webServer']
27
+ export const inject = ['systemPrompt', 'apiProxy', 'webServer', 'agents', 'commands']
26
28
 
27
29
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
28
- export const TASK_BOARD_GUIDANCE = '本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。'
30
+ export const TASK_BOARD_GUIDANCE = '本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。若你同时用 todo_write 维护会话顶部的可见计划列表,最终回复前必须再次调用 todo_write 收尾:没有剩余工作时不要保留 in_progress,已完成的最后一步要标为 completed。'
29
31
 
30
32
  /**
31
33
  * Settings namespace of the board's announcement capability — the section the
@@ -87,7 +89,15 @@ const DEFAULT_ANNOUNCE = true
87
89
  export const apply = mountOnce('@linxin666/dsh-client-ui-task-board', applyImpl)
88
90
 
89
91
  function applyImpl(ctx: Context, config?: Config): void {
90
- const host = new TaskBoardHostService(ctx.apiProxy)
92
+ const host = new TaskBoardHostService(ctx.apiProxy, {
93
+ commandDispatcher: {
94
+ async execute(sessionId, line, signal) {
95
+ const agent = ctx.agents.get(sessionId)
96
+ if (agent === undefined) throw new Error(`execution session ${sessionId} is not available`)
97
+ return (await ctx.commands.execute(agent, line, [], signal))?.result
98
+ },
99
+ },
100
+ })
91
101
  host.setConfiguration(config?.enabled ?? true, config?.preventIdleSleep ?? false)
92
102
  host.start()
93
103
  ctx.effect(() => {