@anastops/mcp-server 0.1.0 → 1.1.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.
Files changed (106) hide show
  1. package/dist/formatters.d.ts.map +1 -1
  2. package/dist/formatters.js +12 -3
  3. package/dist/formatters.js.map +1 -1
  4. package/dist/handlers/agent-handlers.d.ts +8 -0
  5. package/dist/handlers/agent-handlers.d.ts.map +1 -0
  6. package/dist/handlers/agent-handlers.js +184 -0
  7. package/dist/handlers/agent-handlers.js.map +1 -0
  8. package/dist/handlers/artifact-handlers.d.ts +8 -0
  9. package/dist/handlers/artifact-handlers.d.ts.map +1 -0
  10. package/dist/handlers/artifact-handlers.js +122 -0
  11. package/dist/handlers/artifact-handlers.js.map +1 -0
  12. package/dist/handlers/cost-handlers.d.ts +8 -0
  13. package/dist/handlers/cost-handlers.d.ts.map +1 -0
  14. package/dist/handlers/cost-handlers.js +140 -0
  15. package/dist/handlers/cost-handlers.js.map +1 -0
  16. package/dist/handlers/handlers.agent.d.ts +10 -0
  17. package/dist/handlers/handlers.agent.d.ts.map +1 -0
  18. package/dist/handlers/handlers.agent.js +99 -0
  19. package/dist/handlers/handlers.agent.js.map +1 -0
  20. package/dist/handlers/handlers.base.d.ts +83 -0
  21. package/dist/handlers/handlers.base.d.ts.map +1 -0
  22. package/dist/handlers/handlers.base.js +351 -0
  23. package/dist/handlers/handlers.base.js.map +1 -0
  24. package/dist/handlers/handlers.lock.d.ts +8 -0
  25. package/dist/handlers/handlers.lock.d.ts.map +1 -0
  26. package/dist/handlers/handlers.lock.js +111 -0
  27. package/dist/handlers/handlers.lock.js.map +1 -0
  28. package/dist/handlers/handlers.memory.d.ts +11 -0
  29. package/dist/handlers/handlers.memory.d.ts.map +1 -0
  30. package/dist/handlers/handlers.memory.js +122 -0
  31. package/dist/handlers/handlers.memory.js.map +1 -0
  32. package/dist/handlers/handlers.monitoring.d.ts +8 -0
  33. package/dist/handlers/handlers.monitoring.d.ts.map +1 -0
  34. package/dist/handlers/handlers.monitoring.js +99 -0
  35. package/dist/handlers/handlers.monitoring.js.map +1 -0
  36. package/dist/handlers/handlers.orchestration.d.ts +9 -0
  37. package/dist/handlers/handlers.orchestration.d.ts.map +1 -0
  38. package/dist/handlers/handlers.orchestration.js +128 -0
  39. package/dist/handlers/handlers.orchestration.js.map +1 -0
  40. package/dist/handlers/handlers.session.d.ts +18 -0
  41. package/dist/handlers/handlers.session.d.ts.map +1 -0
  42. package/dist/handlers/handlers.session.js +286 -0
  43. package/dist/handlers/handlers.session.js.map +1 -0
  44. package/dist/handlers/handlers.task.d.ts +15 -0
  45. package/dist/handlers/handlers.task.d.ts.map +1 -0
  46. package/dist/handlers/handlers.task.js +762 -0
  47. package/dist/handlers/handlers.task.js.map +1 -0
  48. package/dist/handlers/handlers.utility.d.ts +10 -0
  49. package/dist/handlers/handlers.utility.d.ts.map +1 -0
  50. package/dist/handlers/handlers.utility.js +59 -0
  51. package/dist/handlers/handlers.utility.js.map +1 -0
  52. package/dist/handlers/index.d.ts +18 -0
  53. package/dist/handlers/index.d.ts.map +1 -0
  54. package/dist/handlers/index.js +209 -0
  55. package/dist/handlers/index.js.map +1 -0
  56. package/dist/handlers/lock-handlers.d.ts +8 -0
  57. package/dist/handlers/lock-handlers.d.ts.map +1 -0
  58. package/dist/handlers/lock-handlers.js +154 -0
  59. package/dist/handlers/lock-handlers.js.map +1 -0
  60. package/dist/handlers/memory-handlers.d.ts +8 -0
  61. package/dist/handlers/memory-handlers.d.ts.map +1 -0
  62. package/dist/handlers/memory-handlers.js +76 -0
  63. package/dist/handlers/memory-handlers.js.map +1 -0
  64. package/dist/handlers/orchestration-handlers.d.ts +8 -0
  65. package/dist/handlers/orchestration-handlers.d.ts.map +1 -0
  66. package/dist/handlers/orchestration-handlers.js +113 -0
  67. package/dist/handlers/orchestration-handlers.js.map +1 -0
  68. package/dist/handlers/session-handlers.d.ts +8 -0
  69. package/dist/handlers/session-handlers.d.ts.map +1 -0
  70. package/dist/handlers/session-handlers.js +558 -0
  71. package/dist/handlers/session-handlers.js.map +1 -0
  72. package/dist/handlers/task-handlers.d.ts +8 -0
  73. package/dist/handlers/task-handlers.d.ts.map +1 -0
  74. package/dist/handlers/task-handlers.js +677 -0
  75. package/dist/handlers/task-handlers.js.map +1 -0
  76. package/dist/handlers/tool-definitions.d.ts +2626 -0
  77. package/dist/handlers/tool-definitions.d.ts.map +1 -0
  78. package/dist/handlers/tool-definitions.js +641 -0
  79. package/dist/handlers/tool-definitions.js.map +1 -0
  80. package/dist/handlers/types.d.ts +90 -0
  81. package/dist/handlers/types.d.ts.map +1 -0
  82. package/dist/handlers/types.js +5 -0
  83. package/dist/handlers/types.js.map +1 -0
  84. package/dist/handlers/utility-handlers.d.ts +8 -0
  85. package/dist/handlers/utility-handlers.d.ts.map +1 -0
  86. package/dist/handlers/utility-handlers.js +113 -0
  87. package/dist/handlers/utility-handlers.js.map +1 -0
  88. package/dist/handlers/utils.d.ts +30 -0
  89. package/dist/handlers/utils.d.ts.map +1 -0
  90. package/dist/handlers/utils.js +95 -0
  91. package/dist/handlers/utils.js.map +1 -0
  92. package/dist/handlers.d.ts +17 -2260
  93. package/dist/handlers.d.ts.map +1 -1
  94. package/dist/handlers.js +17 -1836
  95. package/dist/handlers.js.map +1 -1
  96. package/dist/index.js +41 -7
  97. package/dist/index.js.map +1 -1
  98. package/dist/persistence.d.ts +18 -1
  99. package/dist/persistence.d.ts.map +1 -1
  100. package/dist/persistence.js +159 -99
  101. package/dist/persistence.js.map +1 -1
  102. package/dist/schemas.d.ts +299 -0
  103. package/dist/schemas.d.ts.map +1 -0
  104. package/dist/schemas.js +334 -0
  105. package/dist/schemas.js.map +1 -0
  106. package/package.json +11 -8
@@ -0,0 +1,677 @@
1
+ /**
2
+ * Task management handlers
3
+ * Handles: task_create, task_queue, task_status, task_complete, task_list, task_execute, task_cancel, task_retry
4
+ */
5
+ import { nanoid } from 'nanoid';
6
+ import { getTask, safePersist } from './utils.js';
7
+ export const toolDefinitions = [
8
+ {
9
+ name: 'task_create',
10
+ description: 'Create a task with intelligent routing. Supports agents and skills for Claude provider.',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ session_id: { type: 'string' },
15
+ type: { type: 'string' },
16
+ description: { type: 'string' },
17
+ prompt: { type: 'string' },
18
+ context_files: { type: 'array', items: { type: 'string' } },
19
+ force_provider: { type: 'string' },
20
+ force_tier: { type: 'number' },
21
+ agent: {
22
+ type: 'string',
23
+ description: 'Agent name to use (e.g., orchestration-specialist). Agent defines model, tools, and skills.',
24
+ },
25
+ skills: {
26
+ type: 'array',
27
+ items: { type: 'string' },
28
+ description: 'Specific skills to load (e.g., ["anastops-sessions", "anastops-tasks"])',
29
+ },
30
+ },
31
+ required: ['session_id', 'type', 'description', 'prompt'],
32
+ },
33
+ },
34
+ {
35
+ name: 'task_queue',
36
+ description: 'Queue a task for execution',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: { task_id: { type: 'string' } },
40
+ required: ['task_id'],
41
+ },
42
+ },
43
+ {
44
+ name: 'task_status',
45
+ description: 'Get task status',
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: { task_id: { type: 'string' } },
49
+ required: ['task_id'],
50
+ },
51
+ },
52
+ {
53
+ name: 'task_complete',
54
+ description: 'Mark task as completed',
55
+ inputSchema: {
56
+ type: 'object',
57
+ properties: {
58
+ task_id: { type: 'string' },
59
+ content: { type: 'string' },
60
+ artifacts: { type: 'array', items: { type: 'string' } },
61
+ },
62
+ required: ['task_id', 'content'],
63
+ },
64
+ },
65
+ {
66
+ name: 'task_list',
67
+ description: 'List tasks in a session. Use format="table" for beautiful ASCII table output.',
68
+ inputSchema: {
69
+ type: 'object',
70
+ properties: {
71
+ session_id: { type: 'string' },
72
+ status: { type: 'string' },
73
+ format: {
74
+ type: 'string',
75
+ enum: ['table', 'json'],
76
+ description: 'Output format: "table" for ASCII table, "json" for raw JSON (default)',
77
+ },
78
+ },
79
+ required: ['session_id'],
80
+ },
81
+ },
82
+ {
83
+ name: 'task_execute',
84
+ description: 'Execute a pending or queued task',
85
+ inputSchema: {
86
+ type: 'object',
87
+ properties: {
88
+ task_id: { type: 'string', description: 'ID of the task to execute' },
89
+ wait: { type: 'boolean', description: 'Wait for task completion (default: true)' },
90
+ },
91
+ required: ['task_id'],
92
+ },
93
+ },
94
+ {
95
+ name: 'task_cancel',
96
+ description: 'Cancel a pending, queued, or running task',
97
+ inputSchema: {
98
+ type: 'object',
99
+ properties: {
100
+ task_id: { type: 'string', description: 'ID of the task to cancel' },
101
+ },
102
+ required: ['task_id'],
103
+ },
104
+ },
105
+ {
106
+ name: 'task_retry',
107
+ description: 'Retry a failed task',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ task_id: { type: 'string', description: 'ID of the failed task to retry' },
112
+ },
113
+ required: ['task_id'],
114
+ },
115
+ },
116
+ {
117
+ name: 'task_batch_create',
118
+ description: 'Create multiple tasks in a single call. ALWAYS use this instead of multiple task_create calls when creating 2+ tasks.',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ session_id: { type: 'string', description: 'Session to create tasks in' },
123
+ tasks: {
124
+ type: 'array',
125
+ description: 'Array of task definitions (max 50)',
126
+ items: {
127
+ type: 'object',
128
+ properties: {
129
+ type: { type: 'string' },
130
+ description: { type: 'string' },
131
+ prompt: { type: 'string' },
132
+ context_files: { type: 'array', items: { type: 'string' } },
133
+ force_provider: { type: 'string' },
134
+ force_tier: { type: 'number' },
135
+ agent: { type: 'string' },
136
+ skills: { type: 'array', items: { type: 'string' } },
137
+ },
138
+ required: ['type', 'description', 'prompt'],
139
+ },
140
+ },
141
+ },
142
+ required: ['session_id', 'tasks'],
143
+ },
144
+ },
145
+ {
146
+ name: 'task_batch_execute',
147
+ description: 'Execute multiple tasks in parallel. Use after task_batch_create or with existing task IDs. Much more efficient than sequential task_execute calls.',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ task_ids: {
152
+ type: 'array',
153
+ items: { type: 'string' },
154
+ description: 'Array of task IDs to execute (max 50)',
155
+ },
156
+ parallel: {
157
+ type: 'boolean',
158
+ description: 'Execute tasks in parallel (default: true). Set false for sequential.',
159
+ },
160
+ wait: {
161
+ type: 'boolean',
162
+ description: 'Wait for all tasks to complete (default: true)',
163
+ },
164
+ },
165
+ required: ['task_ids'],
166
+ },
167
+ },
168
+ ];
169
+ export async function handleTool(name, args, context) {
170
+ switch (name) {
171
+ case 'task_create': {
172
+ const taskId = nanoid(21);
173
+ const now = new Date();
174
+ const taskType = args['type'] ?? 'other';
175
+ // Store agent and skills in task input for execution
176
+ const taskInput = {
177
+ prompt: args['prompt'],
178
+ context_files: args['context_files'] ?? [],
179
+ };
180
+ if (args['agent'] !== undefined) {
181
+ taskInput.agent = args['agent'];
182
+ }
183
+ if (args['skills'] !== undefined) {
184
+ taskInput.skills = args['skills'];
185
+ }
186
+ const task = {
187
+ id: taskId,
188
+ session_id: args['session_id'],
189
+ agent_id: null,
190
+ type: taskType,
191
+ status: 'pending',
192
+ description: args['description'],
193
+ input: taskInput,
194
+ output: null,
195
+ error: null,
196
+ complexity_score: 0,
197
+ routing_tier: 3,
198
+ provider: 'claude',
199
+ model: 'claude-sonnet',
200
+ token_usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, cost: 0 },
201
+ created_at: now,
202
+ started_at: null,
203
+ completed_at: null,
204
+ dependencies: [],
205
+ priority: 5,
206
+ retry_count: 0,
207
+ max_retries: 3,
208
+ failover_history: [],
209
+ failover_enabled: args['disable_failover'] !== true,
210
+ };
211
+ // Use default routing (simplified for now)
212
+ const provider = args['force_provider'] ?? 'claude';
213
+ const model = 'claude-sonnet';
214
+ const tier = args['force_tier'] ?? 3;
215
+ task.provider = provider;
216
+ task.model = model;
217
+ task.routing_tier = tier;
218
+ task.complexity_score = 50;
219
+ context.tasks.set(taskId, task);
220
+ // Persist to MongoDB
221
+ safePersist(context.getPersistence().saveTask(task));
222
+ const response = {
223
+ task_id: task.id,
224
+ type: task.type,
225
+ routing: { tier, provider, model },
226
+ };
227
+ if (taskInput.agent !== undefined) {
228
+ response.agent = taskInput.agent;
229
+ }
230
+ return response;
231
+ }
232
+ case 'task_queue': {
233
+ const task = await getTask(args['task_id'], context);
234
+ if (task === null)
235
+ throw new Error('Task not found');
236
+ task.status = 'queued';
237
+ context.tasks.set(task.id, task);
238
+ safePersist(context.getPersistence().saveTask(task));
239
+ return { task_id: task.id, status: task.status };
240
+ }
241
+ case 'task_status': {
242
+ const task = await getTask(args['task_id'], context);
243
+ if (task === null)
244
+ throw new Error('Task not found');
245
+ return task;
246
+ }
247
+ case 'task_complete': {
248
+ const task = await getTask(args['task_id'], context);
249
+ if (task === null)
250
+ throw new Error('Task not found');
251
+ task.status = 'completed';
252
+ task.completed_at = new Date();
253
+ task.output = {
254
+ content: args['content'],
255
+ artifacts: args['artifacts'] ?? [],
256
+ files_modified: [],
257
+ metadata: {},
258
+ };
259
+ context.tasks.set(task.id, task);
260
+ // Persist to MongoDB
261
+ safePersist(context.getPersistence().saveTask(task));
262
+ return { task_id: task.id, status: task.status };
263
+ }
264
+ case 'task_list': {
265
+ // Get from in-memory cache first
266
+ const inMemoryTasks = [];
267
+ for (const [, task] of context.tasks.entries()) {
268
+ if (args['session_id'] === undefined ||
269
+ args['session_id'] === '' ||
270
+ task.session_id === args['session_id']) {
271
+ inMemoryTasks.push(task);
272
+ }
273
+ }
274
+ // Also get from MongoDB for persistence
275
+ const persistedTasks = await context.getPersistence().listTasks({
276
+ ...(args['session_id'] !== undefined && { session_id: args['session_id'] }),
277
+ });
278
+ // Merge: in-memory takes precedence
279
+ const inMemoryIds = new Set(inMemoryTasks.map((t) => t.id));
280
+ const sessionTasks = [
281
+ ...inMemoryTasks,
282
+ ...persistedTasks.filter((t) => !inMemoryIds.has(t.id)),
283
+ ];
284
+ return {
285
+ count: sessionTasks.length,
286
+ tasks: sessionTasks.map((t) => ({
287
+ id: t.id,
288
+ type: t.type,
289
+ status: t.status,
290
+ description: t.description.slice(0, 50),
291
+ })),
292
+ };
293
+ }
294
+ case 'task_execute': {
295
+ const taskId = args['task_id'];
296
+ const wait = args['wait'] ?? true;
297
+ const task = await getTask(taskId, context);
298
+ if (task === null) {
299
+ throw new Error(`Task not found: ${taskId}`);
300
+ }
301
+ if (task.status !== 'pending' && task.status !== 'queued') {
302
+ return {
303
+ task_id: taskId,
304
+ status: task.status,
305
+ error: `Task cannot be executed - current status is '${task.status}'`,
306
+ };
307
+ }
308
+ // Update task status to running
309
+ task.status = 'running';
310
+ task.started_at = new Date();
311
+ context.tasks.set(taskId, task);
312
+ // Persist running status
313
+ safePersist(context.getPersistence().saveTask(task));
314
+ try {
315
+ // Get adapter for the routed provider
316
+ const adapter = context.registry.get(task.provider);
317
+ if (adapter === undefined) {
318
+ throw new Error(`Provider adapter not found: ${task.provider}`);
319
+ }
320
+ // Execute task with adapter
321
+ // Use workspace root from environment or cwd
322
+ const workingDir = process.env['ANASTOPS_WORKSPACE'] ?? process.cwd();
323
+ // Build request with optional agent and skills
324
+ const taskInput = task.input;
325
+ const executeRequest = {
326
+ model: task.model,
327
+ prompt: taskInput?.prompt ?? task.description,
328
+ working_dir: workingDir,
329
+ };
330
+ // Pass agent and skills if specified
331
+ if (taskInput?.agent !== undefined) {
332
+ executeRequest.agent = taskInput.agent;
333
+ }
334
+ if (taskInput?.skills !== undefined) {
335
+ executeRequest.skills = taskInput.skills;
336
+ }
337
+ // Execute request without onProgress (not supported in ExecuteOptions)
338
+ const response = await adapter.execute(executeRequest, {
339
+ workingDir,
340
+ });
341
+ // Update task with result
342
+ task.status = 'completed';
343
+ task.completed_at = new Date();
344
+ task.token_usage = response.usage ?? task.token_usage;
345
+ task.output = {
346
+ content: response.content,
347
+ artifacts: [],
348
+ files_modified: [],
349
+ metadata: { usage: response.usage },
350
+ };
351
+ context.tasks.set(taskId, task);
352
+ // Persist completed task
353
+ safePersist(context.getPersistence().saveTask(task));
354
+ // Update session metadata via sessionManager
355
+ if (context.sessionManager.exists(task.session_id)) {
356
+ const session = context.sessionManager.getSession(task.session_id);
357
+ session.metadata = session.metadata ?? {
358
+ total_tokens: 0,
359
+ total_cost: 0,
360
+ agents_used: [],
361
+ files_affected: [],
362
+ tasks_completed: 0,
363
+ tasks_failed: 0,
364
+ };
365
+ session.metadata.tasks_completed = (session.metadata.tasks_completed ?? 0) + 1;
366
+ session.metadata.total_tokens =
367
+ (session.metadata.total_tokens ?? 0) + (response.usage?.total_tokens ?? 0);
368
+ session.metadata.total_cost =
369
+ (session.metadata.total_cost ?? 0) + (response.usage?.cost ?? 0);
370
+ session.updated_at = new Date();
371
+ safePersist(context.getPersistence().saveSession(session));
372
+ }
373
+ return {
374
+ task_id: taskId,
375
+ status: 'completed',
376
+ result: response,
377
+ waited: wait,
378
+ };
379
+ }
380
+ catch (error) {
381
+ task.status = 'failed';
382
+ task.completed_at = new Date();
383
+ task.error = error instanceof Error ? error.message : String(error);
384
+ context.tasks.set(taskId, task);
385
+ // Persist failed task
386
+ safePersist(context.getPersistence().saveTask(task));
387
+ // Update session metadata for failure via sessionManager
388
+ if (context.sessionManager.exists(task.session_id)) {
389
+ const session = context.sessionManager.getSession(task.session_id);
390
+ session.metadata = session.metadata ?? {
391
+ total_tokens: 0,
392
+ total_cost: 0,
393
+ agents_used: [],
394
+ files_affected: [],
395
+ tasks_completed: 0,
396
+ tasks_failed: 0,
397
+ };
398
+ session.metadata.tasks_failed = (session.metadata.tasks_failed ?? 0) + 1;
399
+ session.updated_at = new Date();
400
+ safePersist(context.getPersistence().saveSession(session));
401
+ }
402
+ throw error;
403
+ }
404
+ }
405
+ case 'task_cancel': {
406
+ const taskId = args['task_id'];
407
+ const task = await getTask(taskId, context);
408
+ if (task === null) {
409
+ throw new Error(`Task not found: ${taskId}`);
410
+ }
411
+ // Check if task is cancelable (pending, queued, or running)
412
+ const cancelableStatuses = ['pending', 'queued', 'running'];
413
+ if (!cancelableStatuses.includes(task.status)) {
414
+ return {
415
+ task_id: taskId,
416
+ status: task.status,
417
+ cancelled: false,
418
+ error: `Task cannot be cancelled - current status is '${task.status}'`,
419
+ };
420
+ }
421
+ // Cancel the task
422
+ task.status = 'cancelled';
423
+ task.completed_at = new Date();
424
+ context.tasks.set(taskId, task);
425
+ // Persist to MongoDB
426
+ safePersist(context.getPersistence().saveTask(task));
427
+ return {
428
+ task_id: taskId,
429
+ status: task.status,
430
+ cancelled: true,
431
+ };
432
+ }
433
+ case 'task_retry': {
434
+ const taskId = args['task_id'];
435
+ const task = await getTask(taskId, context);
436
+ if (task === null) {
437
+ throw new Error(`Task not found: ${taskId}`);
438
+ }
439
+ // Check if task is in failed status
440
+ if (task.status !== 'failed') {
441
+ return {
442
+ task_id: taskId,
443
+ status: task.status,
444
+ retried: false,
445
+ error: `Task cannot be retried - current status is '${task.status}' (must be 'failed')`,
446
+ };
447
+ }
448
+ // Check if max retries exceeded
449
+ if (task.retry_count >= task.max_retries) {
450
+ return {
451
+ task_id: taskId,
452
+ status: task.status,
453
+ retried: false,
454
+ error: `Task has exceeded max retries (${task.max_retries})`,
455
+ retry_count: task.retry_count,
456
+ };
457
+ }
458
+ // Retry the task
459
+ task.status = 'pending';
460
+ task.retry_count += 1;
461
+ task.error = null;
462
+ task.started_at = null;
463
+ task.completed_at = null;
464
+ context.tasks.set(taskId, task);
465
+ // Persist to MongoDB
466
+ safePersist(context.getPersistence().saveTask(task));
467
+ return {
468
+ task_id: taskId,
469
+ status: task.status,
470
+ retried: true,
471
+ retry_count: task.retry_count,
472
+ max_retries: task.max_retries,
473
+ };
474
+ }
475
+ case 'task_batch_create': {
476
+ const sessionId = args['session_id'];
477
+ const taskDefs = args['tasks'];
478
+ const createdTasks = [];
479
+ const now = new Date();
480
+ for (const def of taskDefs) {
481
+ const taskId = nanoid(21);
482
+ const taskType = def.type ?? 'other';
483
+ // Store agent and skills in task input for execution
484
+ const taskInput = {
485
+ prompt: def.prompt,
486
+ context_files: def.context_files ?? [],
487
+ };
488
+ if (def.agent !== undefined) {
489
+ taskInput.agent = def.agent;
490
+ }
491
+ if (def.skills !== undefined) {
492
+ taskInput.skills = def.skills;
493
+ }
494
+ const provider = def.force_provider ?? 'claude';
495
+ const model = 'claude-sonnet';
496
+ const tier = def.force_tier ?? 3;
497
+ const task = {
498
+ id: taskId,
499
+ session_id: sessionId,
500
+ agent_id: null,
501
+ type: taskType,
502
+ status: 'pending',
503
+ description: def.description,
504
+ input: taskInput,
505
+ output: null,
506
+ error: null,
507
+ complexity_score: 50,
508
+ routing_tier: tier,
509
+ provider: provider,
510
+ model: model,
511
+ token_usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, cost: 0 },
512
+ created_at: now,
513
+ started_at: null,
514
+ completed_at: null,
515
+ dependencies: [],
516
+ priority: 5,
517
+ retry_count: 0,
518
+ max_retries: 3,
519
+ failover_history: [],
520
+ failover_enabled: true,
521
+ };
522
+ context.tasks.set(taskId, task);
523
+ // Persist to MongoDB
524
+ safePersist(context.getPersistence().saveTask(task));
525
+ const createdTask = {
526
+ task_id: taskId,
527
+ type: task.type,
528
+ routing: { tier, provider, model },
529
+ };
530
+ if (taskInput.agent !== undefined) {
531
+ createdTask.agent = taskInput.agent;
532
+ }
533
+ createdTasks.push(createdTask);
534
+ }
535
+ return {
536
+ created: createdTasks.length,
537
+ task_ids: createdTasks.map((t) => t.task_id),
538
+ tasks: createdTasks,
539
+ };
540
+ }
541
+ case 'task_batch_execute': {
542
+ const taskIds = args['task_ids'];
543
+ const parallel = args['parallel'] ?? true;
544
+ const wait = args['wait'] ?? true;
545
+ // Helper to execute a single task and return result
546
+ const executeOne = async (taskId) => {
547
+ const task = await getTask(taskId, context);
548
+ if (task === null) {
549
+ return { task_id: taskId, status: 'not_found', error: `Task not found: ${taskId}` };
550
+ }
551
+ if (task.status !== 'pending' && task.status !== 'queued') {
552
+ return {
553
+ task_id: taskId,
554
+ status: task.status,
555
+ error: `Task cannot be executed - current status is '${task.status}'`,
556
+ };
557
+ }
558
+ // Update task status to running
559
+ task.status = 'running';
560
+ task.started_at = new Date();
561
+ context.tasks.set(taskId, task);
562
+ safePersist(context.getPersistence().saveTask(task));
563
+ try {
564
+ // Get adapter for the routed provider
565
+ const adapter = context.registry.get(task.provider);
566
+ if (adapter === undefined) {
567
+ throw new Error(`Provider adapter not found: ${task.provider}`);
568
+ }
569
+ const workingDir = process.env['ANASTOPS_WORKSPACE'] ?? process.cwd();
570
+ const taskInput = task.input;
571
+ const executeRequest = {
572
+ model: task.model,
573
+ prompt: taskInput?.prompt ?? task.description,
574
+ working_dir: workingDir,
575
+ };
576
+ if (taskInput?.agent !== undefined) {
577
+ executeRequest.agent = taskInput.agent;
578
+ }
579
+ if (taskInput?.skills !== undefined) {
580
+ executeRequest.skills = taskInput.skills;
581
+ }
582
+ const response = await adapter.execute(executeRequest, { workingDir });
583
+ // Update task with result
584
+ task.status = 'completed';
585
+ task.completed_at = new Date();
586
+ task.token_usage = response.usage ?? task.token_usage;
587
+ task.output = {
588
+ content: response.content,
589
+ artifacts: [],
590
+ files_modified: [],
591
+ metadata: { usage: response.usage },
592
+ };
593
+ context.tasks.set(taskId, task);
594
+ safePersist(context.getPersistence().saveTask(task));
595
+ // Update session metadata
596
+ if (context.sessionManager.exists(task.session_id)) {
597
+ const session = context.sessionManager.getSession(task.session_id);
598
+ session.metadata = session.metadata ?? {
599
+ total_tokens: 0,
600
+ total_cost: 0,
601
+ agents_used: [],
602
+ files_affected: [],
603
+ tasks_completed: 0,
604
+ tasks_failed: 0,
605
+ };
606
+ session.metadata.tasks_completed = (session.metadata.tasks_completed ?? 0) + 1;
607
+ session.metadata.total_tokens =
608
+ (session.metadata.total_tokens ?? 0) + (response.usage?.total_tokens ?? 0);
609
+ session.metadata.total_cost =
610
+ (session.metadata.total_cost ?? 0) + (response.usage?.cost ?? 0);
611
+ session.updated_at = new Date();
612
+ safePersist(context.getPersistence().saveSession(session));
613
+ }
614
+ return {
615
+ task_id: taskId,
616
+ status: 'completed',
617
+ result: { content: response.content, usage: response.usage },
618
+ };
619
+ }
620
+ catch (error) {
621
+ task.status = 'failed';
622
+ task.completed_at = new Date();
623
+ task.error = error instanceof Error ? error.message : String(error);
624
+ context.tasks.set(taskId, task);
625
+ safePersist(context.getPersistence().saveTask(task));
626
+ // Update session metadata for failure
627
+ if (context.sessionManager.exists(task.session_id)) {
628
+ const session = context.sessionManager.getSession(task.session_id);
629
+ session.metadata = session.metadata ?? {
630
+ total_tokens: 0,
631
+ total_cost: 0,
632
+ agents_used: [],
633
+ files_affected: [],
634
+ tasks_completed: 0,
635
+ tasks_failed: 0,
636
+ };
637
+ session.metadata.tasks_failed = (session.metadata.tasks_failed ?? 0) + 1;
638
+ session.updated_at = new Date();
639
+ safePersist(context.getPersistence().saveSession(session));
640
+ }
641
+ return {
642
+ task_id: taskId,
643
+ status: 'failed',
644
+ error: error instanceof Error ? error.message : String(error),
645
+ };
646
+ }
647
+ };
648
+ // Execute tasks either in parallel or sequentially
649
+ let results;
650
+ if (parallel) {
651
+ results = await Promise.all(taskIds.map(executeOne));
652
+ }
653
+ else {
654
+ results = [];
655
+ for (const taskId of taskIds) {
656
+ results.push(await executeOne(taskId));
657
+ }
658
+ }
659
+ // Calculate summary
660
+ const summary = {
661
+ completed: results.filter((r) => r.status === 'completed').length,
662
+ failed: results.filter((r) => r.status === 'failed').length,
663
+ skipped: results.filter((r) => r.status !== 'completed' && r.status !== 'failed').length,
664
+ };
665
+ return {
666
+ executed: results.length,
667
+ parallel,
668
+ waited: wait,
669
+ results,
670
+ summary,
671
+ };
672
+ }
673
+ default:
674
+ throw new Error(`Unknown task tool: ${name}`);
675
+ }
676
+ }
677
+ //# sourceMappingURL=task-handlers.js.map