agent-tower 0.5.2-beta.2 → 0.5.2-beta.3
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/dist/app.d.ts.map +1 -1
- package/dist/app.js +9 -2
- package/dist/app.js.map +1 -1
- package/dist/app.test.js +67 -0
- package/dist/app.test.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/container.d.ts +2 -0
- package/dist/core/container.d.ts.map +1 -1
- package/dist/core/container.js +9 -1
- package/dist/core/container.js.map +1 -1
- package/dist/core/event-bus.d.ts +2 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/executors/__tests__/base.executor.test.d.ts +2 -0
- package/dist/executors/__tests__/base.executor.test.d.ts.map +1 -0
- package/dist/executors/__tests__/base.executor.test.js +264 -0
- package/dist/executors/__tests__/base.executor.test.js.map +1 -0
- package/dist/executors/__tests__/codex.executor.test.js +18 -1
- package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
- package/dist/executors/base.executor.d.ts.map +1 -1
- package/dist/executors/base.executor.js +61 -33
- package/dist/executors/base.executor.js.map +1 -1
- package/dist/executors/codex.executor.d.ts.map +1 -1
- package/dist/executors/codex.executor.js +3 -4
- package/dist/executors/codex.executor.js.map +1 -1
- package/dist/executors/execution-env.d.ts +7 -1
- package/dist/executors/execution-env.d.ts.map +1 -1
- package/dist/executors/execution-env.js +41 -2
- package/dist/executors/execution-env.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/types.d.ts +6 -6
- package/dist/routes/__tests__/tasks.test.js +179 -0
- package/dist/routes/__tests__/tasks.test.js.map +1 -1
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +41 -1
- package/dist/routes/git.js.map +1 -1
- package/dist/routes/tasks.d.ts.map +1 -1
- package/dist/routes/tasks.js +10 -2
- package/dist/routes/tasks.js.map +1 -1
- package/dist/services/__tests__/project.service.test.js +85 -1
- package/dist/services/__tests__/project.service.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.team-run.test.js +84 -1
- package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
- package/dist/services/__tests__/task.service.test.js +190 -0
- package/dist/services/__tests__/task.service.test.js.map +1 -1
- package/dist/services/__tests__/team-reconciler.service.test.js +2 -2
- package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
- package/dist/services/__tests__/team-run.service.test.js +75 -1
- package/dist/services/__tests__/team-run.service.test.js.map +1 -1
- package/dist/services/__tests__/team-scheduler.service.test.js +46 -5
- package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
- package/dist/services/__tests__/workspace-git-watcher.service.test.d.ts +2 -0
- package/dist/services/__tests__/workspace-git-watcher.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/workspace-git-watcher.service.test.js +148 -0
- package/dist/services/__tests__/workspace-git-watcher.service.test.js.map +1 -0
- package/dist/services/__tests__/workspace.service.test.js +43 -1
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/project.service.d.ts +28 -24
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +11 -0
- package/dist/services/project.service.js.map +1 -1
- package/dist/services/session-manager.d.ts +44 -43
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +19 -3
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/task-cleanup.service.d.ts +3 -1
- package/dist/services/task-cleanup.service.d.ts.map +1 -1
- package/dist/services/task-cleanup.service.js +6 -1
- package/dist/services/task-cleanup.service.js.map +1 -1
- package/dist/services/task.service.d.ts +96 -65
- package/dist/services/task.service.d.ts.map +1 -1
- package/dist/services/task.service.js +208 -15
- package/dist/services/task.service.js.map +1 -1
- package/dist/services/team-run.service.d.ts +2 -0
- package/dist/services/team-run.service.d.ts.map +1 -1
- package/dist/services/team-run.service.js +38 -9
- package/dist/services/team-run.service.js.map +1 -1
- package/dist/services/team-scheduler.service.d.ts.map +1 -1
- package/dist/services/team-scheduler.service.js +16 -4
- package/dist/services/team-scheduler.service.js.map +1 -1
- package/dist/services/workspace-git-watcher.service.d.ts +62 -0
- package/dist/services/workspace-git-watcher.service.d.ts.map +1 -0
- package/dist/services/workspace-git-watcher.service.js +427 -0
- package/dist/services/workspace-git-watcher.service.js.map +1 -0
- package/dist/services/workspace.service.d.ts +99 -108
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +43 -6
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/socket/events.d.ts +1 -1
- package/dist/socket/events.d.ts.map +1 -1
- package/dist/socket/events.js.map +1 -1
- package/dist/socket/socket-gateway.d.ts.map +1 -1
- package/dist/socket/socket-gateway.js +5 -1
- package/dist/socket/socket-gateway.js.map +1 -1
- package/dist/web/assets/AgentDemoPage-DRtgA1-w.js +1 -0
- package/dist/web/assets/{DemoPage-PD8AX5pI.js → DemoPage-8g4kqlV1.js} +3 -3
- package/dist/web/assets/GeneralSettingsPage-b2dViAif.js +1 -0
- package/dist/web/assets/{MemberAvatar-BDXmryjB.js → MemberAvatar-BuaKw8fE.js} +1 -1
- package/dist/web/assets/NotificationSettingsPage-DCtnFBrk.js +1 -0
- package/dist/web/assets/ProfileSettingsPage-Hplbudpu.js +3 -0
- package/dist/web/assets/ProjectKanbanPage-UJVkISeV.js +89 -0
- package/dist/web/assets/ProjectSettingsPage-CnG-6FVF.js +2 -0
- package/dist/web/assets/ProviderSettingsPage-B-u7Av-v.js +54 -0
- package/dist/web/assets/SettingsMasterDetail-C7L4KTOf.js +1 -0
- package/dist/web/assets/TeamSettingsPage-DCsq_ojZ.js +1 -0
- package/dist/web/assets/arrow-left-BSle2OYV.js +1 -0
- package/dist/web/assets/check-COf1UbPe.js +1 -0
- package/dist/web/assets/chevron-down-Dj8_pYCl.js +1 -0
- package/dist/web/assets/chevron-right-BUCnUjsN.js +1 -0
- package/dist/web/assets/{chevron-up-BEHjZ-SB.js → chevron-up-BhzmceCN.js} +1 -1
- package/dist/web/assets/{code-block-OCS4YCEC-6FHTyCN2.js → code-block-OCS4YCEC-C9ZDvx5q.js} +1 -1
- package/dist/web/assets/{confirm-dialog-B_wk_zqm.js → confirm-dialog-NY6uLJRk.js} +1 -1
- package/dist/web/assets/folder-picker-B5loyQsl.js +1 -0
- package/dist/web/assets/index-3iFMFw1l.css +1 -0
- package/dist/web/assets/index-DSFOeuuj.js +13 -0
- package/dist/web/assets/input-D67geHKZ.js +1 -0
- package/dist/web/assets/layers-Dl9LLqoY.js +1 -0
- package/dist/web/assets/loader-circle-CP1oxYjb.js +1 -0
- package/dist/web/assets/{mermaid-NOHMQCX5-fubZbKyF.js → mermaid-NOHMQCX5-C-bBE9vu.js} +4 -4
- package/dist/web/assets/message-square-Bwt_cqko.js +1 -0
- package/dist/web/assets/modal-0iwqEf_V.js +1 -0
- package/dist/web/assets/{pencil-uRBgB4qA.js → pencil-CAgGPSMf.js} +1 -1
- package/dist/web/assets/rotate-ccw-CnfGwAnp.js +1 -0
- package/dist/web/assets/{select-BMlH2AuL.js → select-C0X1iRxk.js} +1 -1
- package/dist/web/assets/switch-D_KOTssd.js +1 -0
- package/dist/web/assets/textarea-DS-Evqsv.js +1 -0
- package/dist/web/assets/{upload-B-Mpvu9j.js → upload-CTsJ3wDk.js} +1 -1
- package/dist/web/assets/{use-profiles-D6cZwU1R.js → use-profiles-CzdxiQuG.js} +1 -1
- package/dist/web/assets/{use-providers-DVP6emIi.js → use-providers-BdxKwaE3.js} +1 -1
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +4 -4
- package/node_modules/@agent-tower/shared/dist/socket/events.d.ts +9 -0
- package/node_modules/@agent-tower/shared/dist/socket/events.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/socket/events.js +1 -0
- package/node_modules/@agent-tower/shared/dist/socket/events.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts +21 -0
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/node_modules/@prisma/client/.prisma/client/edge.js +2 -2
- package/node_modules/@prisma/client/.prisma/client/index.js +2 -2
- package/package.json +1 -1
- package/dist/web/assets/AgentDemoPage-Co63rtls.js +0 -1
- package/dist/web/assets/GeneralSettingsPage-C5eiEeTZ.js +0 -1
- package/dist/web/assets/NotificationSettingsPage-BjjPan2M.js +0 -1
- package/dist/web/assets/ProfileSettingsPage-BRpeNq6u.js +0 -3
- package/dist/web/assets/ProjectKanbanPage-DUUu2EYy.js +0 -89
- package/dist/web/assets/ProjectSettingsPage-avctMKfk.js +0 -2
- package/dist/web/assets/ProviderSettingsPage-BIUYN2e0.js +0 -54
- package/dist/web/assets/SettingsSection-pLJ3msrT.js +0 -1
- package/dist/web/assets/TeamSettingsPage-BjKW9nT8.js +0 -1
- package/dist/web/assets/arrow-left-lFz8nyZM.js +0 -1
- package/dist/web/assets/button-BUA8P726.js +0 -1
- package/dist/web/assets/check-BYuuXc71.js +0 -1
- package/dist/web/assets/chevron-down-B931AgE2.js +0 -1
- package/dist/web/assets/chevron-right-F9i4wey6.js +0 -1
- package/dist/web/assets/circle-check-C7xXtl8B.js +0 -1
- package/dist/web/assets/folder-picker-BEHS3b2Q.js +0 -1
- package/dist/web/assets/index-DEQhT5sD.css +0 -1
- package/dist/web/assets/index-RxbHMWXV.js +0 -13
- package/dist/web/assets/loader-circle-stJcjnm-.js +0 -1
- package/dist/web/assets/message-square-C8lb-TDo.js +0 -1
- package/dist/web/assets/modal-UGrjU63G.js +0 -1
- package/dist/web/assets/rotate-ccw-CWaMrTz4.js +0 -1
|
@@ -2,6 +2,7 @@ import { TaskStatus } from '../types/index.js';
|
|
|
2
2
|
import type { EventBus } from '../core/event-bus.js';
|
|
3
3
|
import type { SessionManager } from './session-manager.js';
|
|
4
4
|
import type { TaskCleanupService } from './task-cleanup.service.js';
|
|
5
|
+
import type { WorkspaceGitWatcherService } from './workspace-git-watcher.service.js';
|
|
5
6
|
interface CreateTaskInput {
|
|
6
7
|
title: string;
|
|
7
8
|
description?: string;
|
|
@@ -17,59 +18,80 @@ interface FindTasksParams {
|
|
|
17
18
|
page?: number;
|
|
18
19
|
limit?: number;
|
|
19
20
|
}
|
|
21
|
+
export declare const TASK_TITLE_MAX_LENGTH = 200;
|
|
22
|
+
export declare const TASK_TITLE_AUTOSPLIT_THRESHOLD = 240;
|
|
23
|
+
export declare const TASK_PREVIEW_MAX_LENGTH = 240;
|
|
24
|
+
export declare const TASK_HISTORICAL_TITLE_BODY_THRESHOLD = 240;
|
|
25
|
+
export declare function buildTextPreview(value: string | null | undefined, maxLength?: number): string;
|
|
20
26
|
export declare class TaskService {
|
|
21
27
|
private readonly eventBus;
|
|
22
28
|
private readonly sessionManager;
|
|
23
|
-
private readonly cleanupService
|
|
24
|
-
|
|
29
|
+
private readonly cleanupService;
|
|
30
|
+
private readonly workspaceGitWatcher;
|
|
31
|
+
constructor(eventBus: EventBus, sessionManager: SessionManager, cleanupService?: Pick<TaskCleanupService, 'trigger'> | undefined, workspaceGitWatcher?: Pick<WorkspaceGitWatcherService, 'unwatchWorkspace'>);
|
|
25
32
|
/**
|
|
26
33
|
* 获取项目的任务列表(支持按状态过滤和分页)
|
|
27
34
|
*/
|
|
28
35
|
findByProjectId(projectId: string, params?: FindTasksParams): Promise<{
|
|
29
36
|
data: ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
id: string;
|
|
38
|
+
createdAt: Date;
|
|
39
|
+
status: string;
|
|
40
|
+
priority: number;
|
|
41
|
+
workspaces: ({
|
|
42
|
+
sessions: {
|
|
43
|
+
agentType: string;
|
|
44
|
+
id: string;
|
|
45
|
+
createdAt: Date;
|
|
46
|
+
status: string;
|
|
47
|
+
updatedAt: Date;
|
|
48
|
+
workspaceId: string;
|
|
49
|
+
variant: string;
|
|
50
|
+
providerId: string | null;
|
|
51
|
+
purpose: string;
|
|
52
|
+
tokenUsage: string | null;
|
|
53
|
+
}[];
|
|
54
|
+
} & {
|
|
46
55
|
id: string;
|
|
47
56
|
createdAt: Date;
|
|
48
|
-
updatedAt: Date;
|
|
49
57
|
status: string;
|
|
50
58
|
taskId: string;
|
|
59
|
+
updatedAt: Date;
|
|
60
|
+
branchName: string;
|
|
61
|
+
worktreePath: string;
|
|
62
|
+
workingDir: string;
|
|
51
63
|
parentWorkspaceId: string | null;
|
|
52
64
|
ownerMemberId: string | null;
|
|
53
|
-
branchName: string;
|
|
54
65
|
baseBranch: string | null;
|
|
55
|
-
worktreePath: string;
|
|
56
66
|
workspaceKind: string;
|
|
57
|
-
workingDir: string;
|
|
58
67
|
commitMessage: string | null;
|
|
59
68
|
previewTarget: string | null;
|
|
60
69
|
hibernatedAt: Date | null;
|
|
61
|
-
}[];
|
|
62
|
-
} & {
|
|
63
|
-
id: string;
|
|
64
|
-
description: string | null;
|
|
65
|
-
createdAt: Date;
|
|
70
|
+
})[];
|
|
66
71
|
updatedAt: Date;
|
|
67
|
-
deletedAt: Date | null;
|
|
68
72
|
title: string;
|
|
69
|
-
status: string;
|
|
70
|
-
priority: number;
|
|
71
73
|
position: number;
|
|
72
74
|
projectId: string;
|
|
75
|
+
project: {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
createdAt: Date;
|
|
79
|
+
description: string | null;
|
|
80
|
+
updatedAt: Date;
|
|
81
|
+
repoDeletedAt: Date | null;
|
|
82
|
+
archivedAt: Date | null;
|
|
83
|
+
repoPath: string;
|
|
84
|
+
repoRemoteUrl: string | null;
|
|
85
|
+
mainBranch: string;
|
|
86
|
+
copyFiles: string | null;
|
|
87
|
+
setupScript: string | null;
|
|
88
|
+
quickCommands: string | null;
|
|
89
|
+
};
|
|
90
|
+
} & {
|
|
91
|
+
title: string;
|
|
92
|
+
titlePreview: string;
|
|
93
|
+
contentPreview: string | undefined;
|
|
94
|
+
isTruncated: boolean;
|
|
73
95
|
})[];
|
|
74
96
|
total: number;
|
|
75
97
|
page: number;
|
|
@@ -80,49 +102,58 @@ export declare class TaskService {
|
|
|
80
102
|
* 获取任务详情
|
|
81
103
|
*/
|
|
82
104
|
findById(id: string): Promise<{
|
|
105
|
+
id: string;
|
|
106
|
+
createdAt: Date;
|
|
107
|
+
status: string;
|
|
108
|
+
priority: number;
|
|
83
109
|
workspaces: ({
|
|
84
110
|
sessions: {
|
|
111
|
+
agentType: string;
|
|
85
112
|
id: string;
|
|
86
113
|
createdAt: Date;
|
|
87
|
-
updatedAt: Date;
|
|
88
114
|
status: string;
|
|
115
|
+
updatedAt: Date;
|
|
89
116
|
workspaceId: string;
|
|
90
|
-
agentType: string;
|
|
91
117
|
variant: string;
|
|
92
118
|
providerId: string | null;
|
|
93
|
-
prompt: string;
|
|
94
119
|
purpose: string;
|
|
95
|
-
logSnapshot: string | null;
|
|
96
120
|
tokenUsage: string | null;
|
|
97
121
|
}[];
|
|
98
122
|
} & {
|
|
99
123
|
id: string;
|
|
100
124
|
createdAt: Date;
|
|
101
|
-
updatedAt: Date;
|
|
102
125
|
status: string;
|
|
103
126
|
taskId: string;
|
|
127
|
+
updatedAt: Date;
|
|
128
|
+
branchName: string;
|
|
129
|
+
worktreePath: string;
|
|
130
|
+
workingDir: string;
|
|
104
131
|
parentWorkspaceId: string | null;
|
|
105
132
|
ownerMemberId: string | null;
|
|
106
|
-
branchName: string;
|
|
107
133
|
baseBranch: string | null;
|
|
108
|
-
worktreePath: string;
|
|
109
134
|
workspaceKind: string;
|
|
110
|
-
workingDir: string;
|
|
111
135
|
commitMessage: string | null;
|
|
112
136
|
previewTarget: string | null;
|
|
113
137
|
hibernatedAt: Date | null;
|
|
114
138
|
})[];
|
|
115
|
-
} & {
|
|
116
|
-
id: string;
|
|
117
|
-
description: string | null;
|
|
118
|
-
createdAt: Date;
|
|
119
139
|
updatedAt: Date;
|
|
120
|
-
deletedAt: Date | null;
|
|
121
140
|
title: string;
|
|
122
|
-
status: string;
|
|
123
|
-
priority: number;
|
|
124
141
|
position: number;
|
|
125
142
|
projectId: string;
|
|
143
|
+
} & {
|
|
144
|
+
title: string;
|
|
145
|
+
titlePreview: string;
|
|
146
|
+
contentPreview: string | undefined;
|
|
147
|
+
isTruncated: boolean;
|
|
148
|
+
}>;
|
|
149
|
+
findBodyById(id: string): Promise<{
|
|
150
|
+
taskId: string;
|
|
151
|
+
title: string;
|
|
152
|
+
titlePreview: string;
|
|
153
|
+
body: string;
|
|
154
|
+
bodySource: string;
|
|
155
|
+
prompt: string;
|
|
156
|
+
isTruncated: boolean;
|
|
126
157
|
}>;
|
|
127
158
|
/**
|
|
128
159
|
* 创建任务
|
|
@@ -131,30 +162,30 @@ export declare class TaskService {
|
|
|
131
162
|
*/
|
|
132
163
|
create(projectId: string, input: CreateTaskInput): Promise<{
|
|
133
164
|
id: string;
|
|
134
|
-
description: string | null;
|
|
135
165
|
createdAt: Date;
|
|
136
|
-
updatedAt: Date;
|
|
137
|
-
deletedAt: Date | null;
|
|
138
|
-
title: string;
|
|
139
166
|
status: string;
|
|
167
|
+
description: string | null;
|
|
140
168
|
priority: number;
|
|
169
|
+
updatedAt: Date;
|
|
170
|
+
title: string;
|
|
141
171
|
position: number;
|
|
142
172
|
projectId: string;
|
|
173
|
+
deletedAt: Date | null;
|
|
143
174
|
}>;
|
|
144
175
|
/**
|
|
145
176
|
* 更新任务基本信息
|
|
146
177
|
*/
|
|
147
178
|
update(id: string, input: UpdateTaskInput): Promise<{
|
|
148
179
|
id: string;
|
|
149
|
-
description: string | null;
|
|
150
180
|
createdAt: Date;
|
|
151
|
-
updatedAt: Date;
|
|
152
|
-
deletedAt: Date | null;
|
|
153
|
-
title: string;
|
|
154
181
|
status: string;
|
|
182
|
+
description: string | null;
|
|
155
183
|
priority: number;
|
|
184
|
+
updatedAt: Date;
|
|
185
|
+
title: string;
|
|
156
186
|
position: number;
|
|
157
187
|
projectId: string;
|
|
188
|
+
deletedAt: Date | null;
|
|
158
189
|
}>;
|
|
159
190
|
/**
|
|
160
191
|
* 更新任务状态(含状态流转校验)
|
|
@@ -162,15 +193,15 @@ export declare class TaskService {
|
|
|
162
193
|
*/
|
|
163
194
|
updateStatus(id: string, status: TaskStatus): Promise<{
|
|
164
195
|
id: string;
|
|
165
|
-
description: string | null;
|
|
166
196
|
createdAt: Date;
|
|
167
|
-
updatedAt: Date;
|
|
168
|
-
deletedAt: Date | null;
|
|
169
|
-
title: string;
|
|
170
197
|
status: string;
|
|
198
|
+
description: string | null;
|
|
171
199
|
priority: number;
|
|
200
|
+
updatedAt: Date;
|
|
201
|
+
title: string;
|
|
172
202
|
position: number;
|
|
173
203
|
projectId: string;
|
|
204
|
+
deletedAt: Date | null;
|
|
174
205
|
}>;
|
|
175
206
|
/**
|
|
176
207
|
* 更新任务位置(用于拖拽排序)
|
|
@@ -178,15 +209,15 @@ export declare class TaskService {
|
|
|
178
209
|
*/
|
|
179
210
|
updatePosition(id: string, position: number, status?: TaskStatus): Promise<{
|
|
180
211
|
id: string;
|
|
181
|
-
description: string | null;
|
|
182
212
|
createdAt: Date;
|
|
183
|
-
updatedAt: Date;
|
|
184
|
-
deletedAt: Date | null;
|
|
185
|
-
title: string;
|
|
186
213
|
status: string;
|
|
214
|
+
description: string | null;
|
|
187
215
|
priority: number;
|
|
216
|
+
updatedAt: Date;
|
|
217
|
+
title: string;
|
|
188
218
|
position: number;
|
|
189
219
|
projectId: string;
|
|
220
|
+
deletedAt: Date | null;
|
|
190
221
|
}>;
|
|
191
222
|
/**
|
|
192
223
|
* 快速删除任务
|
|
@@ -218,15 +249,15 @@ export declare class TaskService {
|
|
|
218
249
|
*/
|
|
219
250
|
retry(id: string): Promise<{
|
|
220
251
|
id: string;
|
|
221
|
-
description: string | null;
|
|
222
252
|
createdAt: Date;
|
|
223
|
-
updatedAt: Date;
|
|
224
|
-
deletedAt: Date | null;
|
|
225
|
-
title: string;
|
|
226
253
|
status: string;
|
|
254
|
+
description: string | null;
|
|
227
255
|
priority: number;
|
|
256
|
+
updatedAt: Date;
|
|
257
|
+
title: string;
|
|
228
258
|
position: number;
|
|
229
259
|
projectId: string;
|
|
260
|
+
deletedAt: Date | null;
|
|
230
261
|
}>;
|
|
231
262
|
/**
|
|
232
263
|
* 发射 task:updated 事件,通知 SocketGateway 转发到前端
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.service.d.ts","sourceRoot":"","sources":["../../src/services/task.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAkD,MAAM,mBAAmB,CAAC;AAO/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"task.service.d.ts","sourceRoot":"","sources":["../../src/services/task.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAkD,MAAM,mBAAmB,CAAC;AAO/F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,2BAA2B,CAAC;AACzF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAKrF,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,oCAAoC,MAAiC,CAAC;AAmInF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,SAA0B,GAAG,MAAM,CAM9G;AAqCD,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;gBAHnB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,cAAc,EAC9B,cAAc,GAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,GAAG,SAAqB,EAC3E,mBAAmB,GAAE,IAAI,CAAC,0BAA0B,EAAE,kBAAkB,CAAmC;IAG9H;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,eAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkErE;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuBnB,YAAY,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAyC7B;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe;;;;;;;;;;;;IA6BtD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe;;;;;;;;;;;;IAiB/C;;;OAGG;IACG,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU;;;;;;;;;;;;IA2CjD;;;OAGG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU;;;;;;;;;;;;IAgCtE;;;;;;;OAOG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM;IA0IvB;;OAEG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM;;;;;;;;IAgD3C;;;;;;;OAOG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;IAgDtB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAGzE"}
|
|
@@ -2,6 +2,7 @@ import { prisma } from '../utils/index.js';
|
|
|
2
2
|
import { TaskStatus, SessionStatus, SessionPurpose, WorkspaceStatus } from '../types/index.js';
|
|
3
3
|
import { getWorkspaceWorkingDir } from './workspace-kind.js';
|
|
4
4
|
import { NotFoundError, ValidationError, InvalidStateTransitionError, } from '../errors.js';
|
|
5
|
+
import { getWorkspaceGitWatcherService } from '../core/container.js';
|
|
5
6
|
import { ensureProjectIsMutable } from './project-guards.js';
|
|
6
7
|
import { defaultTeamLockService } from './team-lock.service.js';
|
|
7
8
|
/**
|
|
@@ -17,21 +18,158 @@ const VALID_TRANSITIONS = {
|
|
|
17
18
|
};
|
|
18
19
|
const CANCELLABLE_DELETE_WORK_REQUEST_STATUSES = ['PENDING_APPROVAL', 'QUEUED', 'STARTED'];
|
|
19
20
|
const CANCELLABLE_DELETE_INVOCATION_STATUSES = ['QUEUED', 'RUNNING', 'SESSION_ENDED', 'WAITING_ROOM_REPLY'];
|
|
21
|
+
export const TASK_TITLE_MAX_LENGTH = 200;
|
|
22
|
+
export const TASK_TITLE_AUTOSPLIT_THRESHOLD = 240;
|
|
23
|
+
export const TASK_PREVIEW_MAX_LENGTH = 240;
|
|
24
|
+
export const TASK_HISTORICAL_TITLE_BODY_THRESHOLD = TASK_TITLE_AUTOSPLIT_THRESHOLD;
|
|
25
|
+
const visibleSessionSummary = {
|
|
26
|
+
where: { purpose: { not: SessionPurpose.COMMIT_MSG } },
|
|
27
|
+
select: {
|
|
28
|
+
id: true,
|
|
29
|
+
workspaceId: true,
|
|
30
|
+
agentType: true,
|
|
31
|
+
variant: true,
|
|
32
|
+
providerId: true,
|
|
33
|
+
status: true,
|
|
34
|
+
purpose: true,
|
|
35
|
+
tokenUsage: true,
|
|
36
|
+
createdAt: true,
|
|
37
|
+
updatedAt: true,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
function compactWhitespace(value) {
|
|
41
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
42
|
+
}
|
|
43
|
+
function truncateWithEllipsis(value, maxLength) {
|
|
44
|
+
if (value.length <= maxLength) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
const limit = Math.max(1, maxLength - 3);
|
|
48
|
+
const sliced = value.slice(0, limit);
|
|
49
|
+
const boundary = Math.max(sliced.lastIndexOf(' '), sliced.lastIndexOf(','), sliced.lastIndexOf('。'), sliced.lastIndexOf(','), sliced.lastIndexOf('.'));
|
|
50
|
+
const candidate = boundary >= Math.floor(limit * 0.6) ? sliced.slice(0, boundary) : sliced;
|
|
51
|
+
return `${candidate.trimEnd()}...`;
|
|
52
|
+
}
|
|
20
53
|
function normalizeTaskTitle(title) {
|
|
21
|
-
const normalized = title
|
|
54
|
+
const normalized = compactWhitespace(title);
|
|
22
55
|
if (normalized.length === 0) {
|
|
23
56
|
throw new ValidationError('Task title is required');
|
|
24
57
|
}
|
|
25
|
-
return normalized;
|
|
58
|
+
return truncateWithEllipsis(normalized, TASK_TITLE_MAX_LENGTH);
|
|
59
|
+
}
|
|
60
|
+
function needsBodySplit(rawTitle, normalizedTitle) {
|
|
61
|
+
if (rawTitle.length > TASK_TITLE_AUTOSPLIT_THRESHOLD || normalizedTitle.length > TASK_TITLE_MAX_LENGTH) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const nonEmptyLines = rawTitle.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
65
|
+
return nonEmptyLines.length > 1 && rawTitle.trim().length > TASK_TITLE_MAX_LENGTH;
|
|
66
|
+
}
|
|
67
|
+
function deriveTitleFromBody(rawBody) {
|
|
68
|
+
const firstLine = rawBody.split(/\r?\n/).map((line) => compactWhitespace(line)).find(Boolean) ?? rawBody;
|
|
69
|
+
const sentenceMatch = firstLine.match(/^(.+?[。!?.!?])(?:\s|$)/u);
|
|
70
|
+
const candidate = sentenceMatch?.[1] ?? firstLine;
|
|
71
|
+
return normalizeTaskTitle(candidate);
|
|
72
|
+
}
|
|
73
|
+
function mergeDescriptionParts(parts) {
|
|
74
|
+
const merged = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const part of parts) {
|
|
77
|
+
const trimmed = part?.trim();
|
|
78
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
merged.push(part);
|
|
82
|
+
seen.add(trimmed);
|
|
83
|
+
}
|
|
84
|
+
return merged.length > 0 ? merged.join('\n\n') : undefined;
|
|
85
|
+
}
|
|
86
|
+
function normalizeCreateTaskInput(input) {
|
|
87
|
+
const rawTitle = input.title;
|
|
88
|
+
const compactTitle = compactWhitespace(rawTitle);
|
|
89
|
+
if (compactTitle.length === 0) {
|
|
90
|
+
throw new ValidationError('Task title is required');
|
|
91
|
+
}
|
|
92
|
+
if (!needsBodySplit(rawTitle, compactTitle)) {
|
|
93
|
+
return {
|
|
94
|
+
title: normalizeTaskTitle(rawTitle),
|
|
95
|
+
description: input.description,
|
|
96
|
+
priority: input.priority,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
title: deriveTitleFromBody(rawTitle),
|
|
101
|
+
description: mergeDescriptionParts([rawTitle, input.description]),
|
|
102
|
+
priority: input.priority,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function normalizeUpdateTaskInput(current, input) {
|
|
106
|
+
if (input.title === undefined) {
|
|
107
|
+
return input;
|
|
108
|
+
}
|
|
109
|
+
const rawTitle = input.title;
|
|
110
|
+
const compactTitle = compactWhitespace(rawTitle);
|
|
111
|
+
if (compactTitle.length === 0) {
|
|
112
|
+
throw new ValidationError('Task title is required');
|
|
113
|
+
}
|
|
114
|
+
if (!needsBodySplit(rawTitle, compactTitle)) {
|
|
115
|
+
return {
|
|
116
|
+
...input,
|
|
117
|
+
title: normalizeTaskTitle(rawTitle),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...input,
|
|
122
|
+
title: deriveTitleFromBody(rawTitle),
|
|
123
|
+
description: mergeDescriptionParts([
|
|
124
|
+
rawTitle,
|
|
125
|
+
input.description,
|
|
126
|
+
current.description,
|
|
127
|
+
]),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function buildTextPreview(value, maxLength = TASK_PREVIEW_MAX_LENGTH) {
|
|
131
|
+
const trimmed = (value ?? '').trim();
|
|
132
|
+
if (trimmed.length <= maxLength) {
|
|
133
|
+
return trimmed;
|
|
134
|
+
}
|
|
135
|
+
return truncateWithEllipsis(compactWhitespace(trimmed), maxLength);
|
|
136
|
+
}
|
|
137
|
+
function withTaskPreviews(task, options = {}) {
|
|
138
|
+
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
139
|
+
const contentPreview = buildTextPreview(task.description);
|
|
140
|
+
const isTruncated = task.title !== titlePreview
|
|
141
|
+
|| Boolean(task.description && task.description !== contentPreview);
|
|
142
|
+
const base = {
|
|
143
|
+
...task,
|
|
144
|
+
title: titlePreview,
|
|
145
|
+
titlePreview,
|
|
146
|
+
contentPreview: contentPreview || undefined,
|
|
147
|
+
isTruncated,
|
|
148
|
+
};
|
|
149
|
+
if (options.omitDescription) {
|
|
150
|
+
const withoutDescription = { ...base };
|
|
151
|
+
delete withoutDescription.description;
|
|
152
|
+
return withoutDescription;
|
|
153
|
+
}
|
|
154
|
+
return base;
|
|
155
|
+
}
|
|
156
|
+
function buildTaskPrompt(task) {
|
|
157
|
+
const title = task.title.trim();
|
|
158
|
+
const description = task.description;
|
|
159
|
+
return description?.trim()
|
|
160
|
+
? [title, description].filter(Boolean).join('\n\n')
|
|
161
|
+
: title;
|
|
26
162
|
}
|
|
27
163
|
export class TaskService {
|
|
28
164
|
eventBus;
|
|
29
165
|
sessionManager;
|
|
30
166
|
cleanupService;
|
|
31
|
-
|
|
167
|
+
workspaceGitWatcher;
|
|
168
|
+
constructor(eventBus, sessionManager, cleanupService = undefined, workspaceGitWatcher = getWorkspaceGitWatcherService()) {
|
|
32
169
|
this.eventBus = eventBus;
|
|
33
170
|
this.sessionManager = sessionManager;
|
|
34
171
|
this.cleanupService = cleanupService;
|
|
172
|
+
this.workspaceGitWatcher = workspaceGitWatcher;
|
|
35
173
|
}
|
|
36
174
|
/**
|
|
37
175
|
* 获取项目的任务列表(支持按状态过滤和分页)
|
|
@@ -63,7 +201,18 @@ export class TaskService {
|
|
|
63
201
|
const [data, total] = await Promise.all([
|
|
64
202
|
prisma.task.findMany({
|
|
65
203
|
where,
|
|
66
|
-
|
|
204
|
+
select: {
|
|
205
|
+
id: true,
|
|
206
|
+
projectId: true,
|
|
207
|
+
title: true,
|
|
208
|
+
status: true,
|
|
209
|
+
priority: true,
|
|
210
|
+
position: true,
|
|
211
|
+
createdAt: true,
|
|
212
|
+
updatedAt: true,
|
|
213
|
+
workspaces: { include: { sessions: visibleSessionSummary } },
|
|
214
|
+
project: true,
|
|
215
|
+
},
|
|
67
216
|
orderBy: [{ updatedAt: 'desc' }],
|
|
68
217
|
skip,
|
|
69
218
|
take: limit,
|
|
@@ -78,7 +227,7 @@ export class TaskService {
|
|
|
78
227
|
return (a.position ?? 0) - (b.position ?? 0);
|
|
79
228
|
});
|
|
80
229
|
return {
|
|
81
|
-
data,
|
|
230
|
+
data: data.map((task) => withTaskPreviews(task, { omitDescription: true })),
|
|
82
231
|
total,
|
|
83
232
|
page,
|
|
84
233
|
limit,
|
|
@@ -91,12 +240,58 @@ export class TaskService {
|
|
|
91
240
|
async findById(id) {
|
|
92
241
|
const task = await prisma.task.findFirst({
|
|
93
242
|
where: { id, deletedAt: null },
|
|
94
|
-
|
|
243
|
+
select: {
|
|
244
|
+
id: true,
|
|
245
|
+
projectId: true,
|
|
246
|
+
title: true,
|
|
247
|
+
status: true,
|
|
248
|
+
priority: true,
|
|
249
|
+
position: true,
|
|
250
|
+
createdAt: true,
|
|
251
|
+
updatedAt: true,
|
|
252
|
+
workspaces: { include: { sessions: visibleSessionSummary } },
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
if (!task) {
|
|
256
|
+
throw new NotFoundError('Task', id);
|
|
257
|
+
}
|
|
258
|
+
return withTaskPreviews(task, { omitDescription: true });
|
|
259
|
+
}
|
|
260
|
+
async findBodyById(id) {
|
|
261
|
+
const task = await prisma.task.findFirst({
|
|
262
|
+
where: { id, deletedAt: null },
|
|
263
|
+
select: {
|
|
264
|
+
id: true,
|
|
265
|
+
title: true,
|
|
266
|
+
description: true,
|
|
267
|
+
},
|
|
95
268
|
});
|
|
96
269
|
if (!task) {
|
|
97
270
|
throw new NotFoundError('Task', id);
|
|
98
271
|
}
|
|
99
|
-
|
|
272
|
+
const hasDescription = Boolean(task.description?.trim());
|
|
273
|
+
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
274
|
+
const usesHistoricalTitleBody = !hasDescription
|
|
275
|
+
&& (task.title.length > TASK_HISTORICAL_TITLE_BODY_THRESHOLD || task.title !== titlePreview);
|
|
276
|
+
const body = hasDescription
|
|
277
|
+
? task.description
|
|
278
|
+
: usesHistoricalTitleBody
|
|
279
|
+
? task.title
|
|
280
|
+
: '';
|
|
281
|
+
const prompt = hasDescription
|
|
282
|
+
? buildTaskPrompt(task)
|
|
283
|
+
: usesHistoricalTitleBody
|
|
284
|
+
? task.title
|
|
285
|
+
: task.title.trim();
|
|
286
|
+
return {
|
|
287
|
+
taskId: task.id,
|
|
288
|
+
title: titlePreview,
|
|
289
|
+
titlePreview,
|
|
290
|
+
body,
|
|
291
|
+
bodySource: hasDescription ? 'description' : usesHistoricalTitleBody ? 'historical_title' : 'none',
|
|
292
|
+
prompt,
|
|
293
|
+
isTruncated: task.title !== titlePreview,
|
|
294
|
+
};
|
|
100
295
|
}
|
|
101
296
|
/**
|
|
102
297
|
* 创建任务
|
|
@@ -104,7 +299,7 @@ export class TaskService {
|
|
|
104
299
|
* - 自动计算 position(同状态下最大 position + 1)
|
|
105
300
|
*/
|
|
106
301
|
async create(projectId, input) {
|
|
107
|
-
const
|
|
302
|
+
const normalizedInput = normalizeCreateTaskInput(input);
|
|
108
303
|
// 校验项目存在
|
|
109
304
|
const project = await prisma.project.findUnique({
|
|
110
305
|
where: { id: projectId },
|
|
@@ -120,9 +315,9 @@ export class TaskService {
|
|
|
120
315
|
});
|
|
121
316
|
return prisma.task.create({
|
|
122
317
|
data: {
|
|
123
|
-
title,
|
|
124
|
-
description:
|
|
125
|
-
priority:
|
|
318
|
+
title: normalizedInput.title,
|
|
319
|
+
description: normalizedInput.description,
|
|
320
|
+
priority: normalizedInput.priority ?? 0,
|
|
126
321
|
position: (maxPosition._max.position ?? 0) + 1,
|
|
127
322
|
projectId,
|
|
128
323
|
},
|
|
@@ -132,10 +327,6 @@ export class TaskService {
|
|
|
132
327
|
* 更新任务基本信息
|
|
133
328
|
*/
|
|
134
329
|
async update(id, input) {
|
|
135
|
-
const normalizedInput = {
|
|
136
|
-
...input,
|
|
137
|
-
...(input.title !== undefined ? { title: normalizeTaskTitle(input.title) } : {}),
|
|
138
|
-
};
|
|
139
330
|
const task = await prisma.task.findFirst({
|
|
140
331
|
where: { id, deletedAt: null },
|
|
141
332
|
include: { project: true },
|
|
@@ -144,6 +335,7 @@ export class TaskService {
|
|
|
144
335
|
throw new NotFoundError('Task', id);
|
|
145
336
|
}
|
|
146
337
|
ensureProjectIsMutable(task.project, 'update tasks');
|
|
338
|
+
const normalizedInput = normalizeUpdateTaskInput(task, input);
|
|
147
339
|
return prisma.task.update({
|
|
148
340
|
where: { id },
|
|
149
341
|
data: normalizedInput,
|
|
@@ -440,6 +632,7 @@ export class TaskService {
|
|
|
440
632
|
where: { id: workspace.id },
|
|
441
633
|
data: { status: WorkspaceStatus.ABANDONED },
|
|
442
634
|
});
|
|
635
|
+
this.workspaceGitWatcher.unwatchWorkspace(workspace.id);
|
|
443
636
|
}
|
|
444
637
|
// 重置 Task 到 TODO
|
|
445
638
|
const updated = await prisma.task.update({
|