@prmichaelsen/task-mcp 0.2.0
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/.env.example +19 -0
- package/AGENT.md +1165 -0
- package/CHANGELOG.md +72 -0
- package/agent/commands/acp.commit.md +511 -0
- package/agent/commands/acp.init.md +376 -0
- package/agent/commands/acp.package-install.md +347 -0
- package/agent/commands/acp.proceed.md +311 -0
- package/agent/commands/acp.report.md +392 -0
- package/agent/commands/acp.status.md +280 -0
- package/agent/commands/acp.sync.md +323 -0
- package/agent/commands/acp.update.md +301 -0
- package/agent/commands/acp.validate.md +385 -0
- package/agent/commands/acp.version-check-for-updates.md +275 -0
- package/agent/commands/acp.version-check.md +190 -0
- package/agent/commands/acp.version-update.md +288 -0
- package/agent/commands/command.template.md +273 -0
- package/agent/commands/git.commit.md +511 -0
- package/agent/commands/git.init.md +513 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/acp-task-execution-requirements.md +555 -0
- package/agent/design/api-dto-design.md +394 -0
- package/agent/design/code-extraction-guide.md +827 -0
- package/agent/design/design.template.md +136 -0
- package/agent/design/requirements.template.md +387 -0
- package/agent/design/rest-api-integration.md +489 -0
- package/agent/design/sdk-export-requirements.md +549 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-{title}.template.md +206 -0
- package/agent/milestones/milestone-2-task-infrastructure.md +232 -0
- package/agent/milestones/milestone-4-autonomous-execution.md +235 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.md +1271 -0
- package/agent/patterns/bootstrap.template.md +1237 -0
- package/agent/patterns/pattern.template.md +364 -0
- package/agent/progress.template.yaml +158 -0
- package/agent/progress.yaml +375 -0
- package/agent/scripts/check-for-updates.sh +88 -0
- package/agent/scripts/install.sh +157 -0
- package/agent/scripts/uninstall.sh +75 -0
- package/agent/scripts/update.sh +139 -0
- package/agent/scripts/version.sh +35 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/task-1-{title}.template.md +225 -0
- package/agent/tasks/task-86-task-data-model-schemas.md +143 -0
- package/agent/tasks/task-87-task-database-service.md +220 -0
- package/agent/tasks/task-88-firebase-client-wrapper.md +139 -0
- package/agent/tasks/task-88-task-execution-engine.md +277 -0
- package/agent/tasks/task-89-mcp-server-implementation.md +197 -0
- package/agent/tasks/task-90-build-configuration.md +146 -0
- package/agent/tasks/task-91-deployment-configuration.md +128 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +191 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +191 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/client.ts.html +1030 -0
- package/coverage/lcov-report/src/constant/collections.ts.html +469 -0
- package/coverage/lcov-report/src/constant/index.html +116 -0
- package/coverage/lcov-report/src/dto/index.html +116 -0
- package/coverage/lcov-report/src/dto/transformers.ts.html +568 -0
- package/coverage/lcov-report/src/index.html +146 -0
- package/coverage/lcov-report/src/schemas/index.html +116 -0
- package/coverage/lcov-report/src/schemas/task.ts.html +547 -0
- package/coverage/lcov-report/src/server-factory.ts.html +418 -0
- package/coverage/lcov-report/src/server.ts.html +289 -0
- package/coverage/lcov-report/src/services/index.html +116 -0
- package/coverage/lcov-report/src/services/task-database.service.ts.html +1495 -0
- package/coverage/lcov-report/src/tools/index.html +236 -0
- package/coverage/lcov-report/src/tools/index.ts.html +292 -0
- package/coverage/lcov-report/src/tools/task-add-message.ts.html +277 -0
- package/coverage/lcov-report/src/tools/task-complete-task-item.ts.html +343 -0
- package/coverage/lcov-report/src/tools/task-create-milestone.ts.html +286 -0
- package/coverage/lcov-report/src/tools/task-create-task-item.ts.html +358 -0
- package/coverage/lcov-report/src/tools/task-get-next-step.ts.html +460 -0
- package/coverage/lcov-report/src/tools/task-get-status.ts.html +316 -0
- package/coverage/lcov-report/src/tools/task-report-completion.ts.html +343 -0
- package/coverage/lcov-report/src/tools/task-update-progress.ts.html +232 -0
- package/coverage/lcov.info +974 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/client.ts.html +1030 -0
- package/coverage/src/constant/collections.ts.html +469 -0
- package/coverage/src/constant/index.html +116 -0
- package/coverage/src/dto/index.html +116 -0
- package/coverage/src/dto/transformers.ts.html +568 -0
- package/coverage/src/index.html +146 -0
- package/coverage/src/schemas/index.html +116 -0
- package/coverage/src/schemas/task.ts.html +547 -0
- package/coverage/src/server-factory.ts.html +418 -0
- package/coverage/src/server.ts.html +289 -0
- package/coverage/src/services/index.html +116 -0
- package/coverage/src/services/task-database.service.ts.html +1495 -0
- package/coverage/src/tools/index.html +236 -0
- package/coverage/src/tools/index.ts.html +292 -0
- package/coverage/src/tools/task-add-message.ts.html +277 -0
- package/coverage/src/tools/task-complete-task-item.ts.html +343 -0
- package/coverage/src/tools/task-create-milestone.ts.html +286 -0
- package/coverage/src/tools/task-create-task-item.ts.html +358 -0
- package/coverage/src/tools/task-get-next-step.ts.html +460 -0
- package/coverage/src/tools/task-get-status.ts.html +316 -0
- package/coverage/src/tools/task-report-completion.ts.html +343 -0
- package/coverage/src/tools/task-update-progress.ts.html +232 -0
- package/firestore.rules +95 -0
- package/jest.config.js +31 -0
- package/package.json +67 -0
- package/src/client.spec.ts +199 -0
- package/src/client.ts +315 -0
- package/src/constant/collections.ts +128 -0
- package/src/dto/index.ts +47 -0
- package/src/dto/task-api.dto.ts +219 -0
- package/src/dto/transformers.spec.ts +462 -0
- package/src/dto/transformers.ts +161 -0
- package/src/schemas/task.ts +154 -0
- package/src/server-factory.spec.ts +70 -0
- package/src/server-factory.ts +111 -0
- package/src/server.ts +68 -0
- package/src/services/task-database.service.e2e.ts +116 -0
- package/src/services/task-database.service.spec.ts +479 -0
- package/src/services/task-database.service.ts +470 -0
- package/src/test-schemas.ts +161 -0
- package/src/tools/index.ts +69 -0
- package/src/tools/task-add-message.ts +64 -0
- package/src/tools/task-complete-task-item.ts +86 -0
- package/src/tools/task-create-milestone.ts +67 -0
- package/src/tools/task-create-task-item.ts +91 -0
- package/src/tools/task-get-next-step.spec.ts +136 -0
- package/src/tools/task-get-next-step.ts +125 -0
- package/src/tools/task-get-status.spec.ts +213 -0
- package/src/tools/task-get-status.ts +77 -0
- package/src/tools/task-report-completion.ts +86 -0
- package/src/tools/task-update-progress.ts +49 -0
- package/src/tools/tools.spec.ts +194 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTO Transformers Unit Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for schema-to-DTO transformation functions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Task, TaskMessage, Milestone, TaskItem } from '../schemas/task.js'
|
|
8
|
+
import {
|
|
9
|
+
toTaskItemApiResponse,
|
|
10
|
+
toMilestoneApiResponse,
|
|
11
|
+
toTaskProgressApiResponse,
|
|
12
|
+
toTaskConfigApiResponse,
|
|
13
|
+
toTaskMetadataApiResponse,
|
|
14
|
+
toTaskApiResponse,
|
|
15
|
+
toTaskMessageApiResponse,
|
|
16
|
+
toTaskListApiResponse,
|
|
17
|
+
toTaskMessageListApiResponse
|
|
18
|
+
} from './transformers.js'
|
|
19
|
+
|
|
20
|
+
describe('DTO Transformers', () => {
|
|
21
|
+
describe('toTaskItemApiResponse', () => {
|
|
22
|
+
it('should transform task item with all fields', () => {
|
|
23
|
+
const item: TaskItem = {
|
|
24
|
+
id: 'item-1',
|
|
25
|
+
name: 'Task Item 1',
|
|
26
|
+
description: 'Description 1',
|
|
27
|
+
status: 'completed',
|
|
28
|
+
estimated_hours: 4,
|
|
29
|
+
completed_at: '2026-02-16T12:00:00Z',
|
|
30
|
+
notes: 'Some notes'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = toTaskItemApiResponse(item)
|
|
34
|
+
|
|
35
|
+
expect(result).toEqual({
|
|
36
|
+
id: 'item-1',
|
|
37
|
+
name: 'Task Item 1',
|
|
38
|
+
description: 'Description 1',
|
|
39
|
+
status: 'completed',
|
|
40
|
+
estimated_hours: 4,
|
|
41
|
+
completed_at: '2026-02-16T12:00:00Z',
|
|
42
|
+
notes: 'Some notes'
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should transform task item with optional fields undefined', () => {
|
|
47
|
+
const item: TaskItem = {
|
|
48
|
+
id: 'item-2',
|
|
49
|
+
name: 'Task Item 2',
|
|
50
|
+
description: 'Description 2',
|
|
51
|
+
status: 'not_started'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = toTaskItemApiResponse(item)
|
|
55
|
+
|
|
56
|
+
expect(result).toEqual({
|
|
57
|
+
id: 'item-2',
|
|
58
|
+
name: 'Task Item 2',
|
|
59
|
+
description: 'Description 2',
|
|
60
|
+
status: 'not_started',
|
|
61
|
+
estimated_hours: undefined,
|
|
62
|
+
completed_at: undefined,
|
|
63
|
+
notes: undefined
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('toMilestoneApiResponse', () => {
|
|
69
|
+
it('should transform milestone with all fields', () => {
|
|
70
|
+
const milestone: Milestone = {
|
|
71
|
+
id: 'milestone-1',
|
|
72
|
+
name: 'Milestone 1',
|
|
73
|
+
description: 'Description 1',
|
|
74
|
+
status: 'completed',
|
|
75
|
+
progress: 100,
|
|
76
|
+
tasks_completed: 5,
|
|
77
|
+
tasks_total: 5,
|
|
78
|
+
started_at: '2026-02-15T10:00:00Z',
|
|
79
|
+
completed_at: '2026-02-16T12:00:00Z'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = toMilestoneApiResponse(milestone)
|
|
83
|
+
|
|
84
|
+
expect(result).toEqual({
|
|
85
|
+
id: 'milestone-1',
|
|
86
|
+
name: 'Milestone 1',
|
|
87
|
+
description: 'Description 1',
|
|
88
|
+
status: 'completed',
|
|
89
|
+
progress: 100,
|
|
90
|
+
tasks_completed: 5,
|
|
91
|
+
tasks_total: 5,
|
|
92
|
+
started_at: '2026-02-15T10:00:00Z',
|
|
93
|
+
completed_at: '2026-02-16T12:00:00Z'
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should transform milestone with optional fields undefined', () => {
|
|
98
|
+
const milestone: Milestone = {
|
|
99
|
+
id: 'milestone-2',
|
|
100
|
+
name: 'Milestone 2',
|
|
101
|
+
description: 'Description 2',
|
|
102
|
+
status: 'not_started',
|
|
103
|
+
progress: 0,
|
|
104
|
+
tasks_completed: 0,
|
|
105
|
+
tasks_total: 3
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = toMilestoneApiResponse(milestone)
|
|
109
|
+
|
|
110
|
+
expect(result.started_at).toBeUndefined()
|
|
111
|
+
expect(result.completed_at).toBeUndefined()
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('toTaskProgressApiResponse', () => {
|
|
116
|
+
it('should transform task progress with milestones and tasks', () => {
|
|
117
|
+
const progress: Task['progress'] = {
|
|
118
|
+
current_milestone: 'milestone-1',
|
|
119
|
+
current_task: 'task-1',
|
|
120
|
+
overall_percentage: 50,
|
|
121
|
+
milestones: [
|
|
122
|
+
{
|
|
123
|
+
id: 'milestone-1',
|
|
124
|
+
name: 'Milestone 1',
|
|
125
|
+
description: 'Description 1',
|
|
126
|
+
status: 'in_progress',
|
|
127
|
+
progress: 50,
|
|
128
|
+
tasks_completed: 2,
|
|
129
|
+
tasks_total: 4
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
tasks: {
|
|
133
|
+
'milestone-1': [
|
|
134
|
+
{
|
|
135
|
+
id: 'task-1',
|
|
136
|
+
name: 'Task 1',
|
|
137
|
+
description: 'Description 1',
|
|
138
|
+
status: 'completed',
|
|
139
|
+
completed_at: '2026-02-16T12:00:00Z'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'task-2',
|
|
143
|
+
name: 'Task 2',
|
|
144
|
+
description: 'Description 2',
|
|
145
|
+
status: 'in_progress'
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = toTaskProgressApiResponse(progress)
|
|
152
|
+
|
|
153
|
+
expect(result.current_milestone).toBe('milestone-1')
|
|
154
|
+
expect(result.current_task).toBe('task-1')
|
|
155
|
+
expect(result.overall_percentage).toBe(50)
|
|
156
|
+
expect(result.milestones).toHaveLength(1)
|
|
157
|
+
expect(result.milestones[0].id).toBe('milestone-1')
|
|
158
|
+
expect(result.tasks['milestone-1']).toHaveLength(2)
|
|
159
|
+
expect(result.tasks['milestone-1'][0].id).toBe('task-1')
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
describe('toTaskConfigApiResponse', () => {
|
|
164
|
+
it('should transform task config with all fields', () => {
|
|
165
|
+
const config: Task['config'] = {
|
|
166
|
+
system_prompt: 'Custom prompt',
|
|
167
|
+
auto_approve: false,
|
|
168
|
+
max_iterations: 100
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const result = toTaskConfigApiResponse(config)
|
|
172
|
+
|
|
173
|
+
expect(result).toEqual({
|
|
174
|
+
system_prompt: 'Custom prompt',
|
|
175
|
+
auto_approve: false,
|
|
176
|
+
max_iterations: 100
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should transform task config with optional fields undefined', () => {
|
|
181
|
+
const config: Task['config'] = {
|
|
182
|
+
system_prompt: '',
|
|
183
|
+
auto_approve: true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const result = toTaskConfigApiResponse(config)
|
|
187
|
+
|
|
188
|
+
expect(result.system_prompt).toBe('')
|
|
189
|
+
expect(result.auto_approve).toBe(true)
|
|
190
|
+
expect(result.max_iterations).toBeUndefined()
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe('toTaskMetadataApiResponse', () => {
|
|
195
|
+
it('should transform metadata with all fields', () => {
|
|
196
|
+
const metadata: Task['metadata'] = {
|
|
197
|
+
conversation_id: 'conv-123',
|
|
198
|
+
parent_task_id: 'task-parent',
|
|
199
|
+
tags: ['urgent', 'backend']
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const result = toTaskMetadataApiResponse(metadata)
|
|
203
|
+
|
|
204
|
+
expect(result).toEqual({
|
|
205
|
+
conversation_id: 'conv-123',
|
|
206
|
+
parent_task_id: 'task-parent',
|
|
207
|
+
tags: ['urgent', 'backend']
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should return undefined for undefined metadata', () => {
|
|
212
|
+
const result = toTaskMetadataApiResponse(undefined)
|
|
213
|
+
expect(result).toBeUndefined()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should transform metadata with optional fields undefined', () => {
|
|
217
|
+
const metadata: Task['metadata'] = {}
|
|
218
|
+
|
|
219
|
+
const result = toTaskMetadataApiResponse(metadata)
|
|
220
|
+
|
|
221
|
+
expect(result).toEqual({
|
|
222
|
+
conversation_id: undefined,
|
|
223
|
+
parent_task_id: undefined,
|
|
224
|
+
tags: undefined
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('toTaskApiResponse', () => {
|
|
230
|
+
it('should transform complete task and exclude execution field', () => {
|
|
231
|
+
const task: Task = {
|
|
232
|
+
id: 'task-123',
|
|
233
|
+
user_id: 'user-456',
|
|
234
|
+
title: 'Test Task',
|
|
235
|
+
description: 'Test Description',
|
|
236
|
+
status: 'in_progress',
|
|
237
|
+
created_at: '2026-02-16T10:00:00Z',
|
|
238
|
+
updated_at: '2026-02-16T12:00:00Z',
|
|
239
|
+
started_at: '2026-02-16T10:30:00Z',
|
|
240
|
+
progress: {
|
|
241
|
+
current_milestone: 'milestone-1',
|
|
242
|
+
current_task: 'task-1',
|
|
243
|
+
overall_percentage: 25,
|
|
244
|
+
milestones: [],
|
|
245
|
+
tasks: {}
|
|
246
|
+
},
|
|
247
|
+
execution: {
|
|
248
|
+
api_messages: [{ role: 'user', content: 'secret' }],
|
|
249
|
+
task_messages: [],
|
|
250
|
+
tool_results: [{ tool: 'secret_tool', result: 'secret' }]
|
|
251
|
+
},
|
|
252
|
+
config: {
|
|
253
|
+
system_prompt: 'Test prompt',
|
|
254
|
+
auto_approve: true,
|
|
255
|
+
max_iterations: 500
|
|
256
|
+
},
|
|
257
|
+
metadata: {
|
|
258
|
+
conversation_id: 'conv-123'
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const result = toTaskApiResponse(task)
|
|
263
|
+
|
|
264
|
+
// Verify all expected fields are present
|
|
265
|
+
expect(result.id).toBe('task-123')
|
|
266
|
+
expect(result.user_id).toBe('user-456')
|
|
267
|
+
expect(result.title).toBe('Test Task')
|
|
268
|
+
expect(result.description).toBe('Test Description')
|
|
269
|
+
expect(result.status).toBe('in_progress')
|
|
270
|
+
expect(result.created_at).toBe('2026-02-16T10:00:00Z')
|
|
271
|
+
expect(result.updated_at).toBe('2026-02-16T12:00:00Z')
|
|
272
|
+
expect(result.started_at).toBe('2026-02-16T10:30:00Z')
|
|
273
|
+
expect(result.progress).toBeDefined()
|
|
274
|
+
expect(result.config).toBeDefined()
|
|
275
|
+
expect(result.metadata).toBeDefined()
|
|
276
|
+
|
|
277
|
+
// Verify execution field is NOT present
|
|
278
|
+
expect((result as any).execution).toBeUndefined()
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('should transform task without optional fields', () => {
|
|
282
|
+
const task: Task = {
|
|
283
|
+
id: 'task-456',
|
|
284
|
+
user_id: 'user-789',
|
|
285
|
+
title: 'Simple Task',
|
|
286
|
+
description: 'Simple Description',
|
|
287
|
+
status: 'not_started',
|
|
288
|
+
created_at: '2026-02-16T10:00:00Z',
|
|
289
|
+
updated_at: '2026-02-16T10:00:00Z',
|
|
290
|
+
progress: {
|
|
291
|
+
current_milestone: '',
|
|
292
|
+
current_task: '',
|
|
293
|
+
overall_percentage: 0,
|
|
294
|
+
milestones: [],
|
|
295
|
+
tasks: {}
|
|
296
|
+
},
|
|
297
|
+
execution: {
|
|
298
|
+
api_messages: [],
|
|
299
|
+
task_messages: [],
|
|
300
|
+
tool_results: []
|
|
301
|
+
},
|
|
302
|
+
config: {
|
|
303
|
+
system_prompt: '',
|
|
304
|
+
auto_approve: true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const result = toTaskApiResponse(task)
|
|
309
|
+
|
|
310
|
+
expect(result.started_at).toBeUndefined()
|
|
311
|
+
expect(result.completed_at).toBeUndefined()
|
|
312
|
+
expect(result.metadata).toBeUndefined()
|
|
313
|
+
expect((result as any).execution).toBeUndefined()
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
describe('toTaskMessageApiResponse', () => {
|
|
318
|
+
it('should transform task message with all fields', () => {
|
|
319
|
+
const message: TaskMessage = {
|
|
320
|
+
id: 'msg-123',
|
|
321
|
+
task_id: 'task-456',
|
|
322
|
+
role: 'assistant',
|
|
323
|
+
content: 'Test message content',
|
|
324
|
+
timestamp: '2026-02-16T12:00:00Z',
|
|
325
|
+
metadata: { source: 'agent' }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const result = toTaskMessageApiResponse(message)
|
|
329
|
+
|
|
330
|
+
expect(result).toEqual({
|
|
331
|
+
id: 'msg-123',
|
|
332
|
+
task_id: 'task-456',
|
|
333
|
+
role: 'assistant',
|
|
334
|
+
content: 'Test message content',
|
|
335
|
+
timestamp: '2026-02-16T12:00:00Z',
|
|
336
|
+
metadata: { source: 'agent' }
|
|
337
|
+
})
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('should transform task message without metadata', () => {
|
|
341
|
+
const message: TaskMessage = {
|
|
342
|
+
id: 'msg-456',
|
|
343
|
+
task_id: 'task-789',
|
|
344
|
+
role: 'user',
|
|
345
|
+
content: 'User message',
|
|
346
|
+
timestamp: '2026-02-16T11:00:00Z'
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const result = toTaskMessageApiResponse(message)
|
|
350
|
+
|
|
351
|
+
expect(result.metadata).toBeUndefined()
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
describe('toTaskListApiResponse', () => {
|
|
356
|
+
it('should transform array of tasks to list response', () => {
|
|
357
|
+
const tasks: Task[] = [
|
|
358
|
+
{
|
|
359
|
+
id: 'task-1',
|
|
360
|
+
user_id: 'user-1',
|
|
361
|
+
title: 'Task 1',
|
|
362
|
+
description: 'Description 1',
|
|
363
|
+
status: 'completed',
|
|
364
|
+
created_at: '2026-02-16T10:00:00Z',
|
|
365
|
+
updated_at: '2026-02-16T12:00:00Z',
|
|
366
|
+
progress: {
|
|
367
|
+
current_milestone: '',
|
|
368
|
+
current_task: '',
|
|
369
|
+
overall_percentage: 100,
|
|
370
|
+
milestones: [],
|
|
371
|
+
tasks: {}
|
|
372
|
+
},
|
|
373
|
+
execution: {
|
|
374
|
+
api_messages: [],
|
|
375
|
+
task_messages: [],
|
|
376
|
+
tool_results: []
|
|
377
|
+
},
|
|
378
|
+
config: {
|
|
379
|
+
system_prompt: '',
|
|
380
|
+
auto_approve: true
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: 'task-2',
|
|
385
|
+
user_id: 'user-1',
|
|
386
|
+
title: 'Task 2',
|
|
387
|
+
description: 'Description 2',
|
|
388
|
+
status: 'in_progress',
|
|
389
|
+
created_at: '2026-02-16T11:00:00Z',
|
|
390
|
+
updated_at: '2026-02-16T12:00:00Z',
|
|
391
|
+
progress: {
|
|
392
|
+
current_milestone: '',
|
|
393
|
+
current_task: '',
|
|
394
|
+
overall_percentage: 50,
|
|
395
|
+
milestones: [],
|
|
396
|
+
tasks: {}
|
|
397
|
+
},
|
|
398
|
+
execution: {
|
|
399
|
+
api_messages: [],
|
|
400
|
+
task_messages: [],
|
|
401
|
+
tool_results: []
|
|
402
|
+
},
|
|
403
|
+
config: {
|
|
404
|
+
system_prompt: '',
|
|
405
|
+
auto_approve: true
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
const result = toTaskListApiResponse(tasks)
|
|
411
|
+
|
|
412
|
+
expect(result.tasks).toHaveLength(2)
|
|
413
|
+
expect(result.total).toBe(2)
|
|
414
|
+
expect(result.tasks[0].id).toBe('task-1')
|
|
415
|
+
expect(result.tasks[1].id).toBe('task-2')
|
|
416
|
+
expect((result.tasks[0] as any).execution).toBeUndefined()
|
|
417
|
+
expect((result.tasks[1] as any).execution).toBeUndefined()
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('should handle empty task array', () => {
|
|
421
|
+
const result = toTaskListApiResponse([])
|
|
422
|
+
|
|
423
|
+
expect(result.tasks).toHaveLength(0)
|
|
424
|
+
expect(result.total).toBe(0)
|
|
425
|
+
})
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
describe('toTaskMessageListApiResponse', () => {
|
|
429
|
+
it('should transform array of messages to list response', () => {
|
|
430
|
+
const messages: TaskMessage[] = [
|
|
431
|
+
{
|
|
432
|
+
id: 'msg-1',
|
|
433
|
+
task_id: 'task-1',
|
|
434
|
+
role: 'user',
|
|
435
|
+
content: 'Message 1',
|
|
436
|
+
timestamp: '2026-02-16T10:00:00Z'
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: 'msg-2',
|
|
440
|
+
task_id: 'task-1',
|
|
441
|
+
role: 'assistant',
|
|
442
|
+
content: 'Message 2',
|
|
443
|
+
timestamp: '2026-02-16T10:01:00Z'
|
|
444
|
+
}
|
|
445
|
+
]
|
|
446
|
+
|
|
447
|
+
const result = toTaskMessageListApiResponse(messages)
|
|
448
|
+
|
|
449
|
+
expect(result.messages).toHaveLength(2)
|
|
450
|
+
expect(result.total).toBe(2)
|
|
451
|
+
expect(result.messages[0].id).toBe('msg-1')
|
|
452
|
+
expect(result.messages[1].id).toBe('msg-2')
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('should handle empty message array', () => {
|
|
456
|
+
const result = toTaskMessageListApiResponse([])
|
|
457
|
+
|
|
458
|
+
expect(result.messages).toHaveLength(0)
|
|
459
|
+
expect(result.total).toBe(0)
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
})
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTO Transformers
|
|
3
|
+
*
|
|
4
|
+
* Functions to transform internal schemas to API response DTOs.
|
|
5
|
+
* These transformers exclude internal fields and ensure API responses
|
|
6
|
+
* match the expected structure for agentbase.me.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Task, TaskMessage, Milestone, TaskItem } from '../schemas/task.js'
|
|
10
|
+
import type {
|
|
11
|
+
TaskApiResponse,
|
|
12
|
+
TaskMessageApiResponse,
|
|
13
|
+
MilestoneApiResponse,
|
|
14
|
+
TaskItemApiResponse,
|
|
15
|
+
TaskProgressApiResponse,
|
|
16
|
+
TaskConfigApiResponse,
|
|
17
|
+
TaskMetadataApiResponse
|
|
18
|
+
} from './task-api.dto.js'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transform Task Item schema to API response DTO
|
|
22
|
+
*/
|
|
23
|
+
export function toTaskItemApiResponse(item: TaskItem): TaskItemApiResponse {
|
|
24
|
+
return {
|
|
25
|
+
id: item.id,
|
|
26
|
+
name: item.name,
|
|
27
|
+
description: item.description,
|
|
28
|
+
status: item.status,
|
|
29
|
+
estimated_hours: item.estimated_hours,
|
|
30
|
+
completed_at: item.completed_at,
|
|
31
|
+
notes: item.notes
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Transform Milestone schema to API response DTO
|
|
37
|
+
*/
|
|
38
|
+
export function toMilestoneApiResponse(milestone: Milestone): MilestoneApiResponse {
|
|
39
|
+
return {
|
|
40
|
+
id: milestone.id,
|
|
41
|
+
name: milestone.name,
|
|
42
|
+
description: milestone.description,
|
|
43
|
+
status: milestone.status,
|
|
44
|
+
progress: milestone.progress,
|
|
45
|
+
tasks_completed: milestone.tasks_completed,
|
|
46
|
+
tasks_total: milestone.tasks_total,
|
|
47
|
+
started_at: milestone.started_at,
|
|
48
|
+
completed_at: milestone.completed_at
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Transform Task Progress schema to API response DTO
|
|
54
|
+
*/
|
|
55
|
+
export function toTaskProgressApiResponse(progress: Task['progress']): TaskProgressApiResponse {
|
|
56
|
+
return {
|
|
57
|
+
current_milestone: progress.current_milestone,
|
|
58
|
+
current_task: progress.current_task,
|
|
59
|
+
overall_percentage: progress.overall_percentage,
|
|
60
|
+
milestones: progress.milestones.map(toMilestoneApiResponse),
|
|
61
|
+
tasks: Object.fromEntries(
|
|
62
|
+
Object.entries(progress.tasks).map(([milestoneId, items]) => [
|
|
63
|
+
milestoneId,
|
|
64
|
+
items.map(toTaskItemApiResponse)
|
|
65
|
+
])
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Transform Task Config schema to API response DTO
|
|
72
|
+
*/
|
|
73
|
+
export function toTaskConfigApiResponse(config: Task['config']): TaskConfigApiResponse {
|
|
74
|
+
return {
|
|
75
|
+
system_prompt: config.system_prompt,
|
|
76
|
+
auto_approve: config.auto_approve,
|
|
77
|
+
max_iterations: config.max_iterations
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Transform Task Metadata schema to API response DTO
|
|
83
|
+
*/
|
|
84
|
+
export function toTaskMetadataApiResponse(
|
|
85
|
+
metadata: Task['metadata']
|
|
86
|
+
): TaskMetadataApiResponse | undefined {
|
|
87
|
+
if (!metadata) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
conversation_id: metadata.conversation_id,
|
|
93
|
+
parent_task_id: metadata.parent_task_id,
|
|
94
|
+
tags: metadata.tags
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Transform internal Task schema to API response DTO
|
|
100
|
+
* Excludes internal execution details (api_messages, tool_results)
|
|
101
|
+
*/
|
|
102
|
+
export function toTaskApiResponse(task: Task): TaskApiResponse {
|
|
103
|
+
return {
|
|
104
|
+
id: task.id,
|
|
105
|
+
user_id: task.user_id,
|
|
106
|
+
title: task.title,
|
|
107
|
+
description: task.description,
|
|
108
|
+
status: task.status,
|
|
109
|
+
created_at: task.created_at,
|
|
110
|
+
updated_at: task.updated_at,
|
|
111
|
+
started_at: task.started_at,
|
|
112
|
+
completed_at: task.completed_at,
|
|
113
|
+
|
|
114
|
+
progress: toTaskProgressApiResponse(task.progress),
|
|
115
|
+
config: toTaskConfigApiResponse(task.config),
|
|
116
|
+
metadata: toTaskMetadataApiResponse(task.metadata)
|
|
117
|
+
|
|
118
|
+
// execution field is intentionally excluded
|
|
119
|
+
// Internal fields (api_messages, tool_results) are not exposed
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Transform internal TaskMessage schema to API response DTO
|
|
125
|
+
*/
|
|
126
|
+
export function toTaskMessageApiResponse(message: TaskMessage): TaskMessageApiResponse {
|
|
127
|
+
return {
|
|
128
|
+
id: message.id,
|
|
129
|
+
task_id: message.task_id,
|
|
130
|
+
role: message.role,
|
|
131
|
+
content: message.content,
|
|
132
|
+
timestamp: message.timestamp,
|
|
133
|
+
metadata: message.metadata
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Transform array of tasks to list response DTO
|
|
139
|
+
*/
|
|
140
|
+
export function toTaskListApiResponse(tasks: Task[]): {
|
|
141
|
+
tasks: TaskApiResponse[]
|
|
142
|
+
total: number
|
|
143
|
+
} {
|
|
144
|
+
return {
|
|
145
|
+
tasks: tasks.map(toTaskApiResponse),
|
|
146
|
+
total: tasks.length
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Transform array of messages to list response DTO
|
|
152
|
+
*/
|
|
153
|
+
export function toTaskMessageListApiResponse(messages: TaskMessage[]): {
|
|
154
|
+
messages: TaskMessageApiResponse[]
|
|
155
|
+
total: number
|
|
156
|
+
} {
|
|
157
|
+
return {
|
|
158
|
+
messages: messages.map(toTaskMessageApiResponse),
|
|
159
|
+
total: messages.length
|
|
160
|
+
}
|
|
161
|
+
}
|