dsh-taskboard 0.6.6 → 0.6.7
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 +301 -294
- package/lib/client.js +937 -931
- package/lib/host/archive-sessions.js +30 -0
- package/lib/host/archive-sessions.js.map +1 -0
- package/lib/host/execution.js +3 -0
- package/lib/host/execution.js.map +1 -1
- package/lib/host/locale.js +17 -0
- package/lib/host/locale.js.map +1 -0
- package/lib/host/routes.js +45 -6
- package/lib/host/routes.js.map +1 -1
- package/lib/host/scheduler.js +2 -0
- package/lib/host/scheduler.js.map +1 -1
- package/lib/host/session-sync.js +4 -1
- package/lib/host/session-sync.js.map +1 -1
- package/lib/host/templates.js +11 -75
- package/lib/host/templates.js.map +1 -1
- package/lib/host/tools.js +17 -4
- package/lib/host/tools.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/builtin-templates.js +155 -0
- package/lib/shared/builtin-templates.js.map +1 -0
- package/lib/shared/protocol.js +39 -2
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +90 -90
- package/src/client/api.ts +5 -1
- package/src/client/board/TaskBoard.tsx +16 -11
- package/src/client/board/TaskDetail.tsx +116 -10
- package/src/client/board/TemplateManager.tsx +22 -10
- package/src/client/controller.ts +23 -3
- package/src/client/i18n/en.ts +18 -0
- package/src/client/i18n/templates.ts +25 -0
- package/src/client/i18n/zh.ts +18 -0
- package/src/client/styles.ts +1 -1
- package/src/host/archive-sessions.ts +18 -0
- package/src/host/execution.ts +4 -1
- package/src/host/locale.ts +44 -0
- package/src/host/routes.ts +40 -7
- package/src/host/scheduler.ts +2 -0
- package/src/host/session-sync.ts +4 -1
- package/src/host/templates.ts +8 -59
- package/src/host/tools.ts +20 -7
- package/src/shared/api.ts +6 -3
- package/src/shared/builtin-templates.ts +153 -0
- package/src/shared/protocol.ts +64 -0
- package/src/shared/version.ts +9 -9
package/src/host/scheduler.ts
CHANGED
|
@@ -136,6 +136,8 @@ export class SchedulerService {
|
|
|
136
136
|
task.comments.push({
|
|
137
137
|
id: newCommentId(),
|
|
138
138
|
body: normalizeBody(`[系统] 定时表达式 ${deadCron} 在 4 年内没有可触发时间,已停用定时;请修正 cron 后重新开启。`),
|
|
139
|
+
systemKey: 'sys.cronDead',
|
|
140
|
+
systemParams: { cron: deadCron },
|
|
139
141
|
version: 1,
|
|
140
142
|
createdAt: now,
|
|
141
143
|
})
|
package/src/host/session-sync.ts
CHANGED
|
@@ -213,7 +213,7 @@ export class ExternalSessionSyncService {
|
|
|
213
213
|
|
|
214
214
|
constructor(private readonly deps: SessionSyncDeps) {
|
|
215
215
|
this.unsubscribe = deps.events.onSessionEvent((sessionId, event, sessionMeta) => {
|
|
216
|
-
|
|
216
|
+
return this.handleSessionEvent(sessionId, event, sessionMeta)
|
|
217
217
|
})
|
|
218
218
|
|
|
219
219
|
const interval = deps.scanIntervalMs ?? DEFAULT_SCAN_INTERVAL_MS
|
|
@@ -628,6 +628,8 @@ export class ExternalSessionSyncService {
|
|
|
628
628
|
task.comments.push({
|
|
629
629
|
id: newCommentId(),
|
|
630
630
|
body: normalizeBody(`[系统] 会话执行异常:${errorMessage.slice(0, 300)};任务已退回待办。`),
|
|
631
|
+
systemKey: 'sys.sessionError',
|
|
632
|
+
systemParams: { error: errorMessage.slice(0, 300) },
|
|
631
633
|
version: 1,
|
|
632
634
|
createdAt: now,
|
|
633
635
|
})
|
|
@@ -639,6 +641,7 @@ export class ExternalSessionSyncService {
|
|
|
639
641
|
task.comments.push({
|
|
640
642
|
id: newCommentId(),
|
|
641
643
|
body: normalizeBody('[系统] 会话执行完毕,已自动进入待验收。'),
|
|
644
|
+
systemKey: 'sys.sessionDone',
|
|
642
645
|
version: 1,
|
|
643
646
|
createdAt: now,
|
|
644
647
|
})
|
package/src/host/templates.ts
CHANGED
|
@@ -10,66 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { readFile } from 'node:fs/promises'
|
|
12
12
|
import type { TaskTemplate } from '../shared/api.ts'
|
|
13
|
+
import { BUILTIN_TEMPLATE_CONTENT, BUILTIN_TEMPLATE_IDS, type BuiltinTemplateId } from '../shared/builtin-templates.ts'
|
|
13
14
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
prompt: [
|
|
22
|
-
'实现以上新功能并按序交接:',
|
|
23
|
-
'1. 明确需求边界与验收标准,列出实现要点',
|
|
24
|
-
'2. 实现功能(含类型定义与错误处理)',
|
|
25
|
-
'3. 补充测试(单测/回归)',
|
|
26
|
-
'4. 运行相关测试套件确认通过',
|
|
27
|
-
].join('\n'),
|
|
28
|
-
urgency: 'normal',
|
|
29
|
-
checklist: ['实现要点已明确(需求边界与验收标准)', '功能已实现并补充测试', '相关测试套件通过'],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
id: 'tpl-bugfix',
|
|
34
|
-
name: 'Bug 修复',
|
|
35
|
-
task: {
|
|
36
|
-
title: '修复:',
|
|
37
|
-
prompt: [
|
|
38
|
-
'修复以上问题并按序交接:',
|
|
39
|
-
'1. 复现问题(写最小复现步骤或测试)',
|
|
40
|
-
'2. 定位根因,说明为什么会发生',
|
|
41
|
-
'3. 修复并补回归测试',
|
|
42
|
-
'4. 运行相关测试套件确认无回归',
|
|
43
|
-
].join('\n'),
|
|
44
|
-
urgency: 'urgent',
|
|
45
|
-
checklist: ['已复现并定位根因', '修复已提交到任务分支', '回归测试通过'],
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: 'tpl-release',
|
|
50
|
-
name: '发布检查',
|
|
51
|
-
task: {
|
|
52
|
-
title: '发布:',
|
|
53
|
-
prompt: '执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。',
|
|
54
|
-
urgency: 'normal',
|
|
55
|
-
checklist: ['版本号已更新(package.json 与版本常量同步)', '构建通过', '全部测试通过', '变更记录已写'],
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
id: 'tpl-patrol',
|
|
60
|
-
name: '例行巡检',
|
|
61
|
-
task: {
|
|
62
|
-
title: '巡检:',
|
|
63
|
-
prompt: [
|
|
64
|
-
'例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。',
|
|
65
|
-
'发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。',
|
|
66
|
-
'输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。',
|
|
67
|
-
].join('\n'),
|
|
68
|
-
urgency: 'relaxed',
|
|
69
|
-
execution: { mode: 'scheduled', cron: '0 9 * * 1' },
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
]
|
|
15
|
+
/**
|
|
16
|
+
* The built-in templates seeded when the side file does not exist yet.
|
|
17
|
+
* Seeded from the shared zh content (the side file is plain data; the client
|
|
18
|
+
* resolves the active locale at render time — see shared/builtin-templates.ts).
|
|
19
|
+
*/
|
|
20
|
+
export const BUILTIN_TEMPLATES: ReadonlyArray<{ id: BuiltinTemplateId; name: string; task: TaskTemplate['task'] }> =
|
|
21
|
+
BUILTIN_TEMPLATE_IDS.map(id => ({ id, name: BUILTIN_TEMPLATE_CONTENT.zh[id].name, task: BUILTIN_TEMPLATE_CONTENT.zh[id].task }))
|
|
73
22
|
|
|
74
23
|
/** Mint a template id. */
|
|
75
24
|
function newTemplateId(): string {
|
package/src/host/tools.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SessionArchiveResult } from '../shared/api.ts'
|
|
2
|
+
import { archiveTaskSessions } from './archive-sessions.ts'
|
|
1
3
|
/**
|
|
2
4
|
* The ten `taskboard_*` agent tools. All writes require a calling agent
|
|
3
5
|
* session (ownership audit), carry optimistic-version checks, and enforce
|
|
@@ -162,6 +164,8 @@ export interface WorkspaceFace {
|
|
|
162
164
|
get(id: string): { id: string; path: string; title: string } | undefined
|
|
163
165
|
/** List all workspaces. */
|
|
164
166
|
list(): Array<{ id: string; path: string; title: string }>
|
|
167
|
+
/** Archive one session durably (when supported by runtime workspaceRegistry). */
|
|
168
|
+
archiveSession?(sessionId: string): Promise<void>
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
/** Adapt the real registry to the narrow face. */
|
|
@@ -178,6 +182,9 @@ export function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {
|
|
|
178
182
|
return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }
|
|
179
183
|
},
|
|
180
184
|
list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),
|
|
185
|
+
...(typeof registry.archiveSession === 'function'
|
|
186
|
+
? { archiveSession: (sessionId: string) => registry.archiveSession(sessionId as Parameters<WorkspaceRegistry['archiveSession']>[0]) }
|
|
187
|
+
: {}),
|
|
181
188
|
}
|
|
182
189
|
}
|
|
183
190
|
|
|
@@ -411,7 +418,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
411
418
|
output: {
|
|
412
419
|
schema: JSON_OUT,
|
|
413
420
|
render: (_args, value) => {
|
|
414
|
-
const v = value as { task?: { id?: string; status?: string; version?: number } }
|
|
421
|
+
const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }
|
|
415
422
|
const t = v.task
|
|
416
423
|
return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]
|
|
417
424
|
},
|
|
@@ -501,7 +508,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
501
508
|
output: {
|
|
502
509
|
schema: JSON_OUT,
|
|
503
510
|
render: (_args, value) => {
|
|
504
|
-
const v = value as { task?: { id?: string; status?: string; version?: number } }
|
|
511
|
+
const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }
|
|
505
512
|
const t = v.task
|
|
506
513
|
return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]
|
|
507
514
|
},
|
|
@@ -554,16 +561,17 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
554
561
|
id: { type: 'string', required: true, description: 'Task id.' },
|
|
555
562
|
status: { type: 'string', required: true, description: 'Target status.' },
|
|
556
563
|
ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },
|
|
564
|
+
archiveSessions: { type: 'boolean', description: 'When moving to archived: whether to archive associated execution sessions as well. Defaults to false.' },
|
|
557
565
|
},
|
|
558
566
|
output: {
|
|
559
567
|
schema: JSON_OUT,
|
|
560
568
|
render: (_args, value) => {
|
|
561
|
-
const v = value as { task?: { id?: string; status?: string; version?: number } }
|
|
569
|
+
const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }
|
|
562
570
|
const t = v.task
|
|
563
|
-
return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}
|
|
571
|
+
return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。${v.sessionArchive === undefined ? '' : ` 会话归档结果:${JSON.stringify(v.sessionArchive)}`}` }]
|
|
564
572
|
},
|
|
565
573
|
},
|
|
566
|
-
async execute(args: { id: string; status: string; ifVersion: number }, exec: unknown) {
|
|
574
|
+
async execute(args: { id: string; status: string; ifVersion: number; archiveSessions?: boolean }, exec: unknown) {
|
|
567
575
|
try {
|
|
568
576
|
const { actor } = caller(exec as ToolRunContext)
|
|
569
577
|
const to = asStatus(args.status)
|
|
@@ -575,9 +583,11 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
575
583
|
: undefined
|
|
576
584
|
// R1: every state guard + the write itself run inside the mutation.
|
|
577
585
|
let next: TaskRecord | undefined
|
|
586
|
+
let beforeTask: TaskRecord | undefined
|
|
578
587
|
await store.mutate('task-moved', ledger => {
|
|
579
588
|
const { index, task } = liveTaskAt(ledger, args.id)
|
|
580
589
|
versionGuard(task, args.ifVersion)
|
|
590
|
+
beforeTask = task
|
|
581
591
|
|
|
582
592
|
// Code-level gate: agents never complete a task.
|
|
583
593
|
if (to === 'done') {
|
|
@@ -607,7 +617,10 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
607
617
|
ledger.tasks[index] = next
|
|
608
618
|
return [next]
|
|
609
619
|
})
|
|
610
|
-
|
|
620
|
+
const sessionArchive = to === 'archived' && args.archiveSessions === true
|
|
621
|
+
? await archiveTaskSessions(beforeTask ?? next!, deps.workspaces.archiveSession)
|
|
622
|
+
: undefined
|
|
623
|
+
return json({ task: summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) })
|
|
611
624
|
} catch (error) { fail(error) }
|
|
612
625
|
},
|
|
613
626
|
})) as () => void)
|
|
@@ -929,4 +942,4 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar
|
|
|
929
942
|
})) as () => void)
|
|
930
943
|
|
|
931
944
|
return disposers
|
|
932
|
-
}
|
|
945
|
+
}
|
package/src/shared/api.ts
CHANGED
|
@@ -38,7 +38,7 @@ export type ApiResult<T> = ApiOk<T> | ApiFail
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
|
|
40
40
|
/** Full-state response (the reconnect baseline after an SSE gap). */
|
|
41
|
-
export type StateResponse = TaskLedger
|
|
41
|
+
export type StateResponse = TaskLedger & { capabilities?: { archiveSessions: boolean } }
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a
|
|
@@ -89,7 +89,10 @@ export type UpdateTaskBody = {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/** Move-task request body (ifVersion mandatory; the user MAY move to done). */
|
|
92
|
-
export type MoveTaskBody = { ifVersion: number; status: string }
|
|
92
|
+
export type MoveTaskBody = { ifVersion: number; status: string; archiveSessions?: boolean }
|
|
93
|
+
|
|
94
|
+
export type SessionArchiveResult = { archived: string[]; failed: Array<{ sessionId: string; error: string }>; unsupported: string[] }
|
|
95
|
+
export type MoveTaskResponse = TaskSummary & { sessionArchive?: SessionArchiveResult }
|
|
93
96
|
|
|
94
97
|
/**
|
|
95
98
|
* Quick-reject request body (card ✗ button): move back to todo plus an
|
|
@@ -270,4 +273,4 @@ export type ChangeEvent = {
|
|
|
270
273
|
revision: number
|
|
271
274
|
kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'
|
|
272
275
|
tasks: TaskSummary[]
|
|
273
|
-
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in task-template content (0.6.4): the four factory templates seeded
|
|
3
|
+
* into the template side file, in both shipped locales. The host seeds the zh
|
|
4
|
+
* copy as the persisted fallback (the side file is plain data); the client
|
|
5
|
+
* resolves the ACTIVE locale's copy at render / prefill time via
|
|
6
|
+
* {@link builtinTemplateContent}, so an English GUI shows English template
|
|
7
|
+
* text without ever re-seeding the side file.
|
|
8
|
+
*
|
|
9
|
+
* Kept in shared/ (not the client i18n dictionaries) because the content is
|
|
10
|
+
* structured task DATA — a checklist is an array and the prompt is multi-line
|
|
11
|
+
* — rather than UI-chrome strings, and because the host needs the same seed
|
|
12
|
+
* content to write the side file.
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-taskboard/shared/builtin-templates
|
|
15
|
+
*/
|
|
16
|
+
import type { TaskTemplateSpec } from './api.ts'
|
|
17
|
+
|
|
18
|
+
/** Shipped built-in template locales (mirrors the client i18n LocaleId). */
|
|
19
|
+
export type BuiltinTemplateLocale = 'zh' | 'en'
|
|
20
|
+
|
|
21
|
+
/** One built-in template's localized name + task spec. */
|
|
22
|
+
export interface BuiltinTemplateContent {
|
|
23
|
+
name: string
|
|
24
|
+
task: TaskTemplateSpec
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Stable ids of the factory templates (persisted in the side file). */
|
|
28
|
+
export const BUILTIN_TEMPLATE_IDS = ['tpl-feature', 'tpl-bugfix', 'tpl-release', 'tpl-patrol'] as const
|
|
29
|
+
|
|
30
|
+
export type BuiltinTemplateId = (typeof BUILTIN_TEMPLATE_IDS)[number]
|
|
31
|
+
|
|
32
|
+
/** Built-in template content per locale (name + prefilled task spec). */
|
|
33
|
+
export const BUILTIN_TEMPLATE_CONTENT: Readonly<Record<BuiltinTemplateLocale, Readonly<Record<BuiltinTemplateId, BuiltinTemplateContent>>>> = {
|
|
34
|
+
zh: {
|
|
35
|
+
'tpl-feature': {
|
|
36
|
+
name: '新增功能',
|
|
37
|
+
task: {
|
|
38
|
+
title: '新增:',
|
|
39
|
+
prompt: [
|
|
40
|
+
'实现以上新功能并按序交接:',
|
|
41
|
+
'1. 明确需求边界与验收标准,列出实现要点',
|
|
42
|
+
'2. 实现功能(含类型定义与错误处理)',
|
|
43
|
+
'3. 补充测试(单测/回归)',
|
|
44
|
+
'4. 运行相关测试套件确认通过',
|
|
45
|
+
].join('\n'),
|
|
46
|
+
urgency: 'normal',
|
|
47
|
+
checklist: ['实现要点已明确(需求边界与验收标准)', '功能已实现并补充测试', '相关测试套件通过'],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'tpl-bugfix': {
|
|
51
|
+
name: 'Bug 修复',
|
|
52
|
+
task: {
|
|
53
|
+
title: '修复:',
|
|
54
|
+
prompt: [
|
|
55
|
+
'修复以上问题并按序交接:',
|
|
56
|
+
'1. 复现问题(写最小复现步骤或测试)',
|
|
57
|
+
'2. 定位根因,说明为什么会发生',
|
|
58
|
+
'3. 修复并补回归测试',
|
|
59
|
+
'4. 运行相关测试套件确认无回归',
|
|
60
|
+
].join('\n'),
|
|
61
|
+
urgency: 'urgent',
|
|
62
|
+
checklist: ['已复现并定位根因', '修复已提交到任务分支', '回归测试通过'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
'tpl-release': {
|
|
66
|
+
name: '发布检查',
|
|
67
|
+
task: {
|
|
68
|
+
title: '发布:',
|
|
69
|
+
prompt: '执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。',
|
|
70
|
+
urgency: 'normal',
|
|
71
|
+
checklist: ['版本号已更新(package.json 与版本常量同步)', '构建通过', '全部测试通过', '变更记录已写'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
'tpl-patrol': {
|
|
75
|
+
name: '例行巡检',
|
|
76
|
+
task: {
|
|
77
|
+
title: '巡检:',
|
|
78
|
+
prompt: [
|
|
79
|
+
'例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。',
|
|
80
|
+
'发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。',
|
|
81
|
+
'输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。',
|
|
82
|
+
].join('\n'),
|
|
83
|
+
urgency: 'relaxed',
|
|
84
|
+
execution: { mode: 'scheduled', cron: '0 9 * * 1' },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
en: {
|
|
89
|
+
'tpl-feature': {
|
|
90
|
+
name: 'New feature',
|
|
91
|
+
task: {
|
|
92
|
+
title: 'New feature:',
|
|
93
|
+
prompt: [
|
|
94
|
+
'Implement the new feature above and hand off in order:',
|
|
95
|
+
'1. Clarify the requirement boundaries and acceptance criteria; list the implementation points',
|
|
96
|
+
'2. Implement the feature (including type definitions and error handling)',
|
|
97
|
+
'3. Add tests (unit / regression)',
|
|
98
|
+
'4. Run the relevant test suites and confirm they pass',
|
|
99
|
+
].join('\n'),
|
|
100
|
+
urgency: 'normal',
|
|
101
|
+
checklist: ['Implementation points clarified (requirement boundaries and acceptance criteria)', 'Feature implemented with tests added', 'Relevant test suites pass'],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
'tpl-bugfix': {
|
|
105
|
+
name: 'Bug fix',
|
|
106
|
+
task: {
|
|
107
|
+
title: 'Fix:',
|
|
108
|
+
prompt: [
|
|
109
|
+
'Fix the issue above and hand off in order:',
|
|
110
|
+
'1. Reproduce the issue (write minimal reproduction steps or a test)',
|
|
111
|
+
'2. Locate the root cause and explain why it happens',
|
|
112
|
+
'3. Fix it and add regression tests',
|
|
113
|
+
'4. Run the relevant test suites and confirm there are no regressions',
|
|
114
|
+
].join('\n'),
|
|
115
|
+
urgency: 'urgent',
|
|
116
|
+
checklist: ['Issue reproduced and root cause located', 'Fix committed to the task branch', 'Regression tests pass'],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
'tpl-release': {
|
|
120
|
+
name: 'Release check',
|
|
121
|
+
task: {
|
|
122
|
+
title: 'Release:',
|
|
123
|
+
prompt: 'Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).',
|
|
124
|
+
urgency: 'normal',
|
|
125
|
+
checklist: ['Version bumped (package.json and version constants in sync)', 'Build passes', 'All tests pass', 'Changelog written'],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
'tpl-patrol': {
|
|
129
|
+
name: 'Routine patrol',
|
|
130
|
+
task: {
|
|
131
|
+
title: 'Patrol:',
|
|
132
|
+
prompt: [
|
|
133
|
+
'Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.',
|
|
134
|
+
'List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.',
|
|
135
|
+
'Output a patrol summary (use {{lastComments}} to review the last patrol\u2019s conclusions).',
|
|
136
|
+
].join('\n'),
|
|
137
|
+
urgency: 'relaxed',
|
|
138
|
+
execution: { mode: 'scheduled', cron: '0 9 * * 1' },
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve one built-in template's localized content.
|
|
146
|
+
* @param id - the template id.
|
|
147
|
+
* @param locale - the requested locale.
|
|
148
|
+
* @returns the content, or undefined when `id` is not a built-in template id.
|
|
149
|
+
*/
|
|
150
|
+
export function builtinTemplateContent(id: string, locale: BuiltinTemplateLocale): BuiltinTemplateContent | undefined {
|
|
151
|
+
if (!(BUILTIN_TEMPLATE_IDS as readonly string[]).includes(id)) return undefined
|
|
152
|
+
return BUILTIN_TEMPLATE_CONTENT[locale][id as BuiltinTemplateId]
|
|
153
|
+
}
|
package/src/shared/protocol.ts
CHANGED
|
@@ -335,6 +335,15 @@ export type Actor =
|
|
|
335
335
|
| { kind: 'agent'; sessionId: string }
|
|
336
336
|
| { kind: 'system' }
|
|
337
337
|
|
|
338
|
+
/** Structured row of a multi-repo merge system comment (0.6.4). */
|
|
339
|
+
export type SystemCommentRow = {
|
|
340
|
+
/** Repo path relative to the workspace ('' = the workspace root repo). */
|
|
341
|
+
repo: string
|
|
342
|
+
outcome: 'merged' | 'noop' | 'failed'
|
|
343
|
+
/** Failure reason (verbatim) when outcome = 'failed'. */
|
|
344
|
+
error?: string
|
|
345
|
+
}
|
|
346
|
+
|
|
338
347
|
/** A progress/report comment on a task. */
|
|
339
348
|
export type CommentRecord = {
|
|
340
349
|
id: string
|
|
@@ -345,6 +354,16 @@ export type CommentRecord = {
|
|
|
345
354
|
createdAt: number
|
|
346
355
|
/** The session that wrote this comment; absent for user-written ones. */
|
|
347
356
|
threadId?: string
|
|
357
|
+
/**
|
|
358
|
+
* i18n key of a host-generated system message (0.6.4). The GUI localizes it
|
|
359
|
+
* at render time; `body` stays a zh fallback for agent tools / CSV / raw
|
|
360
|
+
* JSON views.
|
|
361
|
+
*/
|
|
362
|
+
systemKey?: string
|
|
363
|
+
/** Flat {name} interpolation params for the system message. */
|
|
364
|
+
systemParams?: Record<string, string>
|
|
365
|
+
/** Structured per-repo rows for the multi-repo merge summary (0.6.4). */
|
|
366
|
+
systemRows?: SystemCommentRow[]
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
/** One commit produced by an isolated execution (hash + subject). */
|
|
@@ -756,6 +775,36 @@ export function syncClaim(task: TaskRecord, to: TaskStatus, now: number, holder?
|
|
|
756
775
|
}
|
|
757
776
|
}
|
|
758
777
|
|
|
778
|
+
/**
|
|
779
|
+
* Collect unique execution session IDs associated with a task:
|
|
780
|
+
* - executions with a non-empty `sessionId`
|
|
781
|
+
* Creator and claim sessions may serve other tasks and are never included.
|
|
782
|
+
* @param task - the task record to inspect.
|
|
783
|
+
* @returns an array of distinct session IDs in stable discovery order.
|
|
784
|
+
*/
|
|
785
|
+
export function taskAssociatedSessionIds(task: TaskRecord): string[] {
|
|
786
|
+
const seen = new Set<string>()
|
|
787
|
+
const result: string[] = []
|
|
788
|
+
const push = (raw: unknown) => {
|
|
789
|
+
if (typeof raw === 'string') {
|
|
790
|
+
const trimmed = raw.trim()
|
|
791
|
+
if (trimmed.length > 0 && !seen.has(trimmed)) {
|
|
792
|
+
seen.add(trimmed)
|
|
793
|
+
result.push(trimmed)
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (Array.isArray(task.executions)) {
|
|
799
|
+
for (const ex of task.executions) {
|
|
800
|
+
if (ex !== null && typeof ex === 'object') {
|
|
801
|
+
push((ex as { sessionId?: unknown }).sessionId)
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return result
|
|
806
|
+
}
|
|
807
|
+
|
|
759
808
|
/**
|
|
760
809
|
* Validate and normalize a pinned model: `{ provider, model, reasoningEffort? }`,
|
|
761
810
|
* provider and model must be non-empty trimmed strings.
|
|
@@ -1020,6 +1069,21 @@ export function validateImportedTask(raw: unknown, now: number): { ok: true; tas
|
|
|
1020
1069
|
version: numOr(ce, 'version', 1),
|
|
1021
1070
|
createdAt: numOr(ce, 'createdAt', now),
|
|
1022
1071
|
...(typeof ce.threadId === 'string' ? { threadId: ce.threadId } : {}),
|
|
1072
|
+
...(typeof ce.systemKey === 'string' && /^sys\.[A-Za-z0-9]+$/.test(ce.systemKey) && ce.systemKey.length <= 100
|
|
1073
|
+
? {
|
|
1074
|
+
systemKey: ce.systemKey,
|
|
1075
|
+
...(typeof ce.systemParams === 'object' && ce.systemParams !== null && !Array.isArray(ce.systemParams)
|
|
1076
|
+
? { systemParams: Object.fromEntries(Object.entries(ce.systemParams).filter(([key, value]) => key.length <= 100 && typeof value === 'string' && value.length <= 4000).slice(0, 20)) as Record<string, string> }
|
|
1077
|
+
: {}),
|
|
1078
|
+
...(Array.isArray(ce.systemRows)
|
|
1079
|
+
? { systemRows: ce.systemRows.filter((row): row is SystemCommentRow => typeof row === 'object' && row !== null
|
|
1080
|
+
&& typeof row.repo === 'string' && (row.repo === '' || isValidRelRepoPath(row.repo))
|
|
1081
|
+
&& ['merged', 'noop', 'failed'].includes(row.outcome)
|
|
1082
|
+
&& (row.error === undefined || typeof row.error === 'string'))
|
|
1083
|
+
.slice(0, MAX_MIRROR_REPOS).map(row => ({ repo: row.repo, outcome: row.outcome, ...(row.error !== undefined ? { error: row.error.slice(0, 4000) } : {}) })) }
|
|
1084
|
+
: {}),
|
|
1085
|
+
}
|
|
1086
|
+
: {}),
|
|
1023
1087
|
})
|
|
1024
1088
|
}
|
|
1025
1089
|
} else return fail('comments must be an array')
|
package/src/shared/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The plugin package version shown in the board UI. Kept in sync with
|
|
3
|
-
* package.json by a regression test (tests lock drift).
|
|
4
|
-
*
|
|
5
|
-
* @module dsh-taskboard/shared/version
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** The package version (must equal package.json "version"). */
|
|
9
|
-
export const PLUGIN_VERSION = '0.6.
|
|
1
|
+
/**
|
|
2
|
+
* The plugin package version shown in the board UI. Kept in sync with
|
|
3
|
+
* package.json by a regression test (tests lock drift).
|
|
4
|
+
*
|
|
5
|
+
* @module dsh-taskboard/shared/version
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** The package version (must equal package.json "version"). */
|
|
9
|
+
export const PLUGIN_VERSION = '0.6.7'
|