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.
Files changed (165) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +9 -2
  3. package/dist/app.js.map +1 -1
  4. package/dist/app.test.js +67 -0
  5. package/dist/app.test.js.map +1 -1
  6. package/dist/cli.js +2 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/container.d.ts +2 -0
  9. package/dist/core/container.d.ts.map +1 -1
  10. package/dist/core/container.js +9 -1
  11. package/dist/core/container.js.map +1 -1
  12. package/dist/core/event-bus.d.ts +2 -1
  13. package/dist/core/event-bus.d.ts.map +1 -1
  14. package/dist/core/event-bus.js.map +1 -1
  15. package/dist/executors/__tests__/base.executor.test.d.ts +2 -0
  16. package/dist/executors/__tests__/base.executor.test.d.ts.map +1 -0
  17. package/dist/executors/__tests__/base.executor.test.js +264 -0
  18. package/dist/executors/__tests__/base.executor.test.js.map +1 -0
  19. package/dist/executors/__tests__/codex.executor.test.js +18 -1
  20. package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
  21. package/dist/executors/base.executor.d.ts.map +1 -1
  22. package/dist/executors/base.executor.js +61 -33
  23. package/dist/executors/base.executor.js.map +1 -1
  24. package/dist/executors/codex.executor.d.ts.map +1 -1
  25. package/dist/executors/codex.executor.js +3 -4
  26. package/dist/executors/codex.executor.js.map +1 -1
  27. package/dist/executors/execution-env.d.ts +7 -1
  28. package/dist/executors/execution-env.d.ts.map +1 -1
  29. package/dist/executors/execution-env.js +41 -2
  30. package/dist/executors/execution-env.js.map +1 -1
  31. package/dist/index.js +2 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/mcp/types.d.ts +6 -6
  34. package/dist/routes/__tests__/tasks.test.js +179 -0
  35. package/dist/routes/__tests__/tasks.test.js.map +1 -1
  36. package/dist/routes/git.d.ts.map +1 -1
  37. package/dist/routes/git.js +41 -1
  38. package/dist/routes/git.js.map +1 -1
  39. package/dist/routes/tasks.d.ts.map +1 -1
  40. package/dist/routes/tasks.js +10 -2
  41. package/dist/routes/tasks.js.map +1 -1
  42. package/dist/services/__tests__/project.service.test.js +85 -1
  43. package/dist/services/__tests__/project.service.test.js.map +1 -1
  44. package/dist/services/__tests__/session-manager.team-run.test.js +84 -1
  45. package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
  46. package/dist/services/__tests__/task.service.test.js +190 -0
  47. package/dist/services/__tests__/task.service.test.js.map +1 -1
  48. package/dist/services/__tests__/team-reconciler.service.test.js +2 -2
  49. package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
  50. package/dist/services/__tests__/team-run.service.test.js +75 -1
  51. package/dist/services/__tests__/team-run.service.test.js.map +1 -1
  52. package/dist/services/__tests__/team-scheduler.service.test.js +46 -5
  53. package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
  54. package/dist/services/__tests__/workspace-git-watcher.service.test.d.ts +2 -0
  55. package/dist/services/__tests__/workspace-git-watcher.service.test.d.ts.map +1 -0
  56. package/dist/services/__tests__/workspace-git-watcher.service.test.js +148 -0
  57. package/dist/services/__tests__/workspace-git-watcher.service.test.js.map +1 -0
  58. package/dist/services/__tests__/workspace.service.test.js +43 -1
  59. package/dist/services/__tests__/workspace.service.test.js.map +1 -1
  60. package/dist/services/project.service.d.ts +28 -24
  61. package/dist/services/project.service.d.ts.map +1 -1
  62. package/dist/services/project.service.js +11 -0
  63. package/dist/services/project.service.js.map +1 -1
  64. package/dist/services/session-manager.d.ts +44 -43
  65. package/dist/services/session-manager.d.ts.map +1 -1
  66. package/dist/services/session-manager.js +19 -3
  67. package/dist/services/session-manager.js.map +1 -1
  68. package/dist/services/task-cleanup.service.d.ts +3 -1
  69. package/dist/services/task-cleanup.service.d.ts.map +1 -1
  70. package/dist/services/task-cleanup.service.js +6 -1
  71. package/dist/services/task-cleanup.service.js.map +1 -1
  72. package/dist/services/task.service.d.ts +96 -65
  73. package/dist/services/task.service.d.ts.map +1 -1
  74. package/dist/services/task.service.js +208 -15
  75. package/dist/services/task.service.js.map +1 -1
  76. package/dist/services/team-run.service.d.ts +2 -0
  77. package/dist/services/team-run.service.d.ts.map +1 -1
  78. package/dist/services/team-run.service.js +38 -9
  79. package/dist/services/team-run.service.js.map +1 -1
  80. package/dist/services/team-scheduler.service.d.ts.map +1 -1
  81. package/dist/services/team-scheduler.service.js +16 -4
  82. package/dist/services/team-scheduler.service.js.map +1 -1
  83. package/dist/services/workspace-git-watcher.service.d.ts +62 -0
  84. package/dist/services/workspace-git-watcher.service.d.ts.map +1 -0
  85. package/dist/services/workspace-git-watcher.service.js +427 -0
  86. package/dist/services/workspace-git-watcher.service.js.map +1 -0
  87. package/dist/services/workspace.service.d.ts +99 -108
  88. package/dist/services/workspace.service.d.ts.map +1 -1
  89. package/dist/services/workspace.service.js +43 -6
  90. package/dist/services/workspace.service.js.map +1 -1
  91. package/dist/socket/events.d.ts +1 -1
  92. package/dist/socket/events.d.ts.map +1 -1
  93. package/dist/socket/events.js.map +1 -1
  94. package/dist/socket/socket-gateway.d.ts.map +1 -1
  95. package/dist/socket/socket-gateway.js +5 -1
  96. package/dist/socket/socket-gateway.js.map +1 -1
  97. package/dist/web/assets/AgentDemoPage-DRtgA1-w.js +1 -0
  98. package/dist/web/assets/{DemoPage-PD8AX5pI.js → DemoPage-8g4kqlV1.js} +3 -3
  99. package/dist/web/assets/GeneralSettingsPage-b2dViAif.js +1 -0
  100. package/dist/web/assets/{MemberAvatar-BDXmryjB.js → MemberAvatar-BuaKw8fE.js} +1 -1
  101. package/dist/web/assets/NotificationSettingsPage-DCtnFBrk.js +1 -0
  102. package/dist/web/assets/ProfileSettingsPage-Hplbudpu.js +3 -0
  103. package/dist/web/assets/ProjectKanbanPage-UJVkISeV.js +89 -0
  104. package/dist/web/assets/ProjectSettingsPage-CnG-6FVF.js +2 -0
  105. package/dist/web/assets/ProviderSettingsPage-B-u7Av-v.js +54 -0
  106. package/dist/web/assets/SettingsMasterDetail-C7L4KTOf.js +1 -0
  107. package/dist/web/assets/TeamSettingsPage-DCsq_ojZ.js +1 -0
  108. package/dist/web/assets/arrow-left-BSle2OYV.js +1 -0
  109. package/dist/web/assets/check-COf1UbPe.js +1 -0
  110. package/dist/web/assets/chevron-down-Dj8_pYCl.js +1 -0
  111. package/dist/web/assets/chevron-right-BUCnUjsN.js +1 -0
  112. package/dist/web/assets/{chevron-up-BEHjZ-SB.js → chevron-up-BhzmceCN.js} +1 -1
  113. package/dist/web/assets/{code-block-OCS4YCEC-6FHTyCN2.js → code-block-OCS4YCEC-C9ZDvx5q.js} +1 -1
  114. package/dist/web/assets/{confirm-dialog-B_wk_zqm.js → confirm-dialog-NY6uLJRk.js} +1 -1
  115. package/dist/web/assets/folder-picker-B5loyQsl.js +1 -0
  116. package/dist/web/assets/index-3iFMFw1l.css +1 -0
  117. package/dist/web/assets/index-DSFOeuuj.js +13 -0
  118. package/dist/web/assets/input-D67geHKZ.js +1 -0
  119. package/dist/web/assets/layers-Dl9LLqoY.js +1 -0
  120. package/dist/web/assets/loader-circle-CP1oxYjb.js +1 -0
  121. package/dist/web/assets/{mermaid-NOHMQCX5-fubZbKyF.js → mermaid-NOHMQCX5-C-bBE9vu.js} +4 -4
  122. package/dist/web/assets/message-square-Bwt_cqko.js +1 -0
  123. package/dist/web/assets/modal-0iwqEf_V.js +1 -0
  124. package/dist/web/assets/{pencil-uRBgB4qA.js → pencil-CAgGPSMf.js} +1 -1
  125. package/dist/web/assets/rotate-ccw-CnfGwAnp.js +1 -0
  126. package/dist/web/assets/{select-BMlH2AuL.js → select-C0X1iRxk.js} +1 -1
  127. package/dist/web/assets/switch-D_KOTssd.js +1 -0
  128. package/dist/web/assets/textarea-DS-Evqsv.js +1 -0
  129. package/dist/web/assets/{upload-B-Mpvu9j.js → upload-CTsJ3wDk.js} +1 -1
  130. package/dist/web/assets/{use-profiles-D6cZwU1R.js → use-profiles-CzdxiQuG.js} +1 -1
  131. package/dist/web/assets/{use-providers-DVP6emIi.js → use-providers-BdxKwaE3.js} +1 -1
  132. package/dist/web/favicon.ico +0 -0
  133. package/dist/web/index.html +4 -4
  134. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts +9 -0
  135. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts.map +1 -1
  136. package/node_modules/@agent-tower/shared/dist/socket/events.js +1 -0
  137. package/node_modules/@agent-tower/shared/dist/socket/events.js.map +1 -1
  138. package/node_modules/@agent-tower/shared/dist/types.d.ts +21 -0
  139. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  140. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  141. package/node_modules/@prisma/client/.prisma/client/edge.js +2 -2
  142. package/node_modules/@prisma/client/.prisma/client/index.js +2 -2
  143. package/package.json +1 -1
  144. package/dist/web/assets/AgentDemoPage-Co63rtls.js +0 -1
  145. package/dist/web/assets/GeneralSettingsPage-C5eiEeTZ.js +0 -1
  146. package/dist/web/assets/NotificationSettingsPage-BjjPan2M.js +0 -1
  147. package/dist/web/assets/ProfileSettingsPage-BRpeNq6u.js +0 -3
  148. package/dist/web/assets/ProjectKanbanPage-DUUu2EYy.js +0 -89
  149. package/dist/web/assets/ProjectSettingsPage-avctMKfk.js +0 -2
  150. package/dist/web/assets/ProviderSettingsPage-BIUYN2e0.js +0 -54
  151. package/dist/web/assets/SettingsSection-pLJ3msrT.js +0 -1
  152. package/dist/web/assets/TeamSettingsPage-BjKW9nT8.js +0 -1
  153. package/dist/web/assets/arrow-left-lFz8nyZM.js +0 -1
  154. package/dist/web/assets/button-BUA8P726.js +0 -1
  155. package/dist/web/assets/check-BYuuXc71.js +0 -1
  156. package/dist/web/assets/chevron-down-B931AgE2.js +0 -1
  157. package/dist/web/assets/chevron-right-F9i4wey6.js +0 -1
  158. package/dist/web/assets/circle-check-C7xXtl8B.js +0 -1
  159. package/dist/web/assets/folder-picker-BEHS3b2Q.js +0 -1
  160. package/dist/web/assets/index-DEQhT5sD.css +0 -1
  161. package/dist/web/assets/index-RxbHMWXV.js +0 -13
  162. package/dist/web/assets/loader-circle-stJcjnm-.js +0 -1
  163. package/dist/web/assets/message-square-C8lb-TDo.js +0 -1
  164. package/dist/web/assets/modal-UGrjU63G.js +0 -1
  165. 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
- constructor(eventBus: EventBus, sessionManager: SessionManager, cleanupService?: Pick<TaskCleanupService, "trigger"> | undefined);
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
- project: {
31
- name: string;
32
- repoDeletedAt: Date | null;
33
- archivedAt: Date | null;
34
- repoRemoteUrl: string | null;
35
- id: string;
36
- description: string | null;
37
- repoPath: string;
38
- mainBranch: string;
39
- copyFiles: string | null;
40
- setupScript: string | null;
41
- quickCommands: string | null;
42
- createdAt: Date;
43
- updatedAt: Date;
44
- };
45
- workspaces: {
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;AAIzF,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;AAwBD,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;gBAFf,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,cAAc,EAC9B,cAAc,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,YAAA;IAGvE;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,eAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuDrE;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAazB;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe;;;;;;;;;;;;IA6BtD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe;;;;;;;;;;;;IAoB/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;;;;;;;;;;;;IA+CtB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAGzE"}
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.trim();
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
- constructor(eventBus, sessionManager, cleanupService) {
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
- include: { workspaces: true, project: true },
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
- include: { workspaces: { include: { sessions: { where: { purpose: { not: SessionPurpose.COMMIT_MSG } } } } } },
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
- return task;
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 title = normalizeTaskTitle(input.title);
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: input.description,
125
- priority: input.priority ?? 0,
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({