musubi-sdd 3.5.1 → 3.6.1

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 (29) hide show
  1. package/README.md +25 -3
  2. package/bin/musubi-orchestrate.js +309 -0
  3. package/package.json +1 -1
  4. package/src/llm-providers/anthropic-provider.js +175 -0
  5. package/src/llm-providers/base-provider.js +221 -0
  6. package/src/llm-providers/copilot-provider.js +262 -0
  7. package/src/llm-providers/index.js +214 -0
  8. package/src/llm-providers/openai-provider.js +205 -0
  9. package/src/orchestration/index.js +25 -0
  10. package/src/orchestration/patterns/swarm.js +111 -4
  11. package/src/orchestration/replanning/adaptive-goal-modifier.js +1150 -0
  12. package/src/orchestration/replanning/alternative-generator.js +508 -0
  13. package/src/orchestration/replanning/config.js +378 -0
  14. package/src/orchestration/replanning/goal-progress-tracker.js +727 -0
  15. package/src/orchestration/replanning/index.js +82 -0
  16. package/src/orchestration/replanning/plan-evaluator.js +455 -0
  17. package/src/orchestration/replanning/plan-monitor.js +379 -0
  18. package/src/orchestration/replanning/proactive-path-optimizer.js +972 -0
  19. package/src/orchestration/replanning/replan-history.js +402 -0
  20. package/src/orchestration/replanning/replanning-engine.js +706 -0
  21. package/src/templates/agents/claude-code/CLAUDE.md +45 -0
  22. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +20 -0
  23. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +89 -0
  24. package/src/templates/agents/codex/AGENTS.md +13 -0
  25. package/src/templates/agents/cursor/AGENTS.md +13 -0
  26. package/src/templates/agents/gemini-cli/GEMINI.md +13 -0
  27. package/src/templates/agents/github-copilot/AGENTS.md +13 -0
  28. package/src/templates/agents/qwen-code/QWEN.md +13 -0
  29. package/src/templates/agents/windsurf/AGENTS.md +13 -0
@@ -0,0 +1,706 @@
1
+ /**
2
+ * @fileoverview Core Replanning Engine for MUSUBI
3
+ * Provides dynamic task replanning capabilities
4
+ * @module orchestration/replanning/replanning-engine
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const EventEmitter = require('events');
11
+ const { ReplanTrigger, ReplanDecision, mergeConfig, defaultReplanningConfig } = require('./config');
12
+ const { PlanMonitor } = require('./plan-monitor');
13
+ const { PlanEvaluator } = require('./plan-evaluator');
14
+ const { AlternativeGenerator } = require('./alternative-generator');
15
+ const { ReplanHistory } = require('./replan-history');
16
+ const { createLLMProvider } = require('../../llm-providers');
17
+
18
+ /**
19
+ * Replanning Engine - Core engine for dynamic task replanning
20
+ */
21
+ class ReplanningEngine extends EventEmitter {
22
+ /**
23
+ * Create a replanning engine
24
+ * @param {Object} [orchestrationEngine] - Reference to orchestration engine
25
+ * @param {Object} [options={}] - Engine options
26
+ */
27
+ constructor(orchestrationEngine = null, options = {}) {
28
+ super();
29
+
30
+ this.engine = orchestrationEngine;
31
+ this.config = mergeConfig(options.config || options);
32
+
33
+ // Initialize components
34
+ this.llmProvider = options.llmProvider || this.createLLMProvider();
35
+ this.monitor = new PlanMonitor({ config: this.config });
36
+ this.evaluator = new PlanEvaluator({ config: this.config.evaluation });
37
+ this.generator = new AlternativeGenerator(this.llmProvider, {
38
+ config: this.config.alternatives,
39
+ scorerConfig: this.config.evaluation
40
+ });
41
+ this.history = new ReplanHistory({ config: this.config.history });
42
+
43
+ // State
44
+ this.currentPlan = null;
45
+ this.planVersion = 0;
46
+ this.isExecuting = false;
47
+ this.executionContext = null;
48
+
49
+ // Wire up monitor events
50
+ this.setupMonitorEvents();
51
+ }
52
+
53
+ /**
54
+ * Create LLM provider based on configuration
55
+ * @returns {LLMProvider} LLM provider instance
56
+ * @private
57
+ */
58
+ createLLMProvider() {
59
+ try {
60
+ const providerConfig = this.config.llmProvider || {};
61
+ return createLLMProvider(providerConfig.provider || 'auto', providerConfig);
62
+ } catch (error) {
63
+ console.warn('Failed to create LLM provider:', error.message);
64
+ // Return a mock provider that warns on use
65
+ return {
66
+ complete: async () => {
67
+ throw new Error('No LLM provider available for replanning');
68
+ },
69
+ completeJSON: async () => {
70
+ throw new Error('No LLM provider available for replanning');
71
+ },
72
+ isAvailable: async () => false
73
+ };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Setup monitor event handlers
79
+ * @private
80
+ */
81
+ setupMonitorEvents() {
82
+ this.monitor.on('trigger', async (trigger) => {
83
+ if (this.config.integration?.emitEvents) {
84
+ this.emit(`${this.config.integration.eventPrefix}:trigger`, trigger);
85
+ }
86
+
87
+ // Handle the trigger
88
+ try {
89
+ await this.handleTrigger(trigger);
90
+ } catch (error) {
91
+ this.emit('error', error);
92
+ }
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Execute a plan with replanning support
98
+ * @param {Object} plan - Execution plan
99
+ * @param {Object} [options={}] - Execution options
100
+ * @returns {Promise<ExecutionResult>} Execution result
101
+ */
102
+ async executeWithReplanning(plan, options = {}) {
103
+ if (!this.config.enabled) {
104
+ // Replanning disabled, delegate to engine
105
+ return this.delegateToEngine(plan, options);
106
+ }
107
+
108
+ this.currentPlan = this.normalizePlan(plan);
109
+ this.planVersion = 0;
110
+ this.isExecuting = true;
111
+
112
+ // Initialize execution context
113
+ this.executionContext = {
114
+ planId: this.currentPlan.id,
115
+ startTime: Date.now(),
116
+ completed: [],
117
+ pending: [...(this.currentPlan.tasks || [])],
118
+ failed: [],
119
+ retries: 0
120
+ };
121
+
122
+ // Record initial snapshot
123
+ this.history.recordSnapshot(
124
+ this.currentPlan.id,
125
+ this.currentPlan,
126
+ 'Initial plan'
127
+ );
128
+
129
+ // Start monitoring
130
+ this.monitor.watch(this.currentPlan.id, {
131
+ plan: this.currentPlan,
132
+ tasks: this.currentPlan.tasks,
133
+ ...this.executionContext
134
+ });
135
+
136
+ try {
137
+ // Execute with replanning loop
138
+ const result = await this.executeLoop(options);
139
+
140
+ this.isExecuting = false;
141
+ this.monitor.unwatch(this.currentPlan.id);
142
+
143
+ return result;
144
+ } catch (error) {
145
+ this.isExecuting = false;
146
+ this.monitor.unwatch(this.currentPlan.id);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Main execution loop with replanning
153
+ * @param {Object} options - Execution options
154
+ * @returns {Promise<ExecutionResult>}
155
+ * @private
156
+ */
157
+ async executeLoop(options) {
158
+ while (this.executionContext.pending.length > 0 && this.isExecuting) {
159
+ const task = this.executionContext.pending.shift();
160
+
161
+ try {
162
+ // Execute task
163
+ const result = await this.executeTask(task, options);
164
+
165
+ // Record success
166
+ this.executionContext.completed.push({
167
+ ...task,
168
+ result,
169
+ duration: result.duration,
170
+ completedAt: Date.now()
171
+ });
172
+
173
+ // Report to monitor
174
+ this.monitor.reportResult(this.currentPlan.id, {
175
+ taskId: task.id,
176
+ status: 'success',
177
+ output: result
178
+ });
179
+
180
+ // Record in evaluator
181
+ this.evaluator.recordExecution(task.skill || task.name, {
182
+ success: true,
183
+ duration: result.duration
184
+ });
185
+
186
+ } catch (error) {
187
+ // Record failure
188
+ const failedTask = {
189
+ ...task,
190
+ error,
191
+ failedAt: Date.now(),
192
+ attempts: (task.attempts || 0) + 1
193
+ };
194
+ this.executionContext.failed.push(failedTask);
195
+
196
+ // Report to monitor (may trigger replanning)
197
+ const trigger = this.monitor.reportResult(this.currentPlan.id, {
198
+ taskId: task.id,
199
+ status: 'failed',
200
+ error
201
+ });
202
+
203
+ // Record in evaluator
204
+ this.evaluator.recordExecution(task.skill || task.name, {
205
+ success: false,
206
+ duration: Date.now() - (task.startTime || Date.now())
207
+ });
208
+
209
+ // If trigger was detected, wait for replanning to complete
210
+ if (trigger) {
211
+ // Replanning is handled by trigger event
212
+ // Continue to next iteration
213
+ }
214
+ }
215
+ }
216
+
217
+ // Generate final result
218
+ return this.generateResult();
219
+ }
220
+
221
+ /**
222
+ * Execute a single task
223
+ * @param {Object} task - Task to execute
224
+ * @param {Object} options - Execution options
225
+ * @returns {Promise<Object>} Task result
226
+ * @private
227
+ */
228
+ async executeTask(task, options) {
229
+ task.startTime = Date.now();
230
+
231
+ if (this.engine) {
232
+ // Delegate to orchestration engine
233
+ return this.engine.executeSkill(task.skill || task.name, task.parameters, options);
234
+ }
235
+
236
+ // Mock execution for testing
237
+ await new Promise(resolve => setTimeout(resolve, 100));
238
+ return {
239
+ success: true,
240
+ duration: Date.now() - task.startTime,
241
+ output: `Executed ${task.skill || task.name}`
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Handle a replanning trigger
247
+ * @param {Object} trigger - Trigger event
248
+ * @returns {Promise<void>}
249
+ * @private
250
+ */
251
+ async handleTrigger(trigger) {
252
+ const failedTask = this.executionContext.failed[this.executionContext.failed.length - 1];
253
+
254
+ // Generate alternatives
255
+ const alternatives = await this.generator.generateAlternatives(
256
+ failedTask,
257
+ this.executionContext
258
+ );
259
+
260
+ // Select best alternative or request human input
261
+ const decision = await this.selectAlternative(alternatives, trigger);
262
+
263
+ // Record the replan event
264
+ const event = this.history.record({
265
+ trigger: trigger.type,
266
+ decision: decision.type,
267
+ planId: this.currentPlan.id,
268
+ failedTask: {
269
+ id: failedTask.id,
270
+ name: failedTask.name,
271
+ skill: failedTask.skill,
272
+ error: failedTask.error?.message
273
+ },
274
+ alternatives: alternatives.map(a => ({
275
+ id: a.id,
276
+ description: a.description,
277
+ confidence: a.confidence
278
+ })),
279
+ selectedAlternative: decision.alternative ? {
280
+ id: decision.alternative.id,
281
+ description: decision.alternative.description,
282
+ confidence: decision.alternative.confidence,
283
+ reasoning: decision.alternative.reasoning
284
+ } : null,
285
+ context: {
286
+ completedCount: this.executionContext.completed.length,
287
+ pendingCount: this.executionContext.pending.length,
288
+ failedCount: this.executionContext.failed.length
289
+ }
290
+ });
291
+
292
+ // Apply the decision
293
+ await this.applyDecision(decision, trigger);
294
+
295
+ // Emit replan event
296
+ this.emit('replan', { event, decision, alternatives });
297
+ }
298
+
299
+ /**
300
+ * Select the best alternative or request human input
301
+ * @param {Alternative[]} alternatives - Available alternatives
302
+ * @param {Object} trigger - Trigger event
303
+ * @returns {Promise<Decision>} Selected decision
304
+ * @private
305
+ */
306
+ async selectAlternative(alternatives, trigger) {
307
+ // Check if human approval is always required for this trigger
308
+ const alwaysApprove = this.config.humanInLoop?.alwaysApprove || [];
309
+ const requiresApproval = alwaysApprove.includes(trigger.type);
310
+
311
+ if (alternatives.length === 0) {
312
+ return {
313
+ type: ReplanDecision.ABORT,
314
+ alternative: null,
315
+ reason: 'No viable alternatives found'
316
+ };
317
+ }
318
+
319
+ const bestAlternative = alternatives[0];
320
+
321
+ // Check if confidence is below threshold
322
+ const threshold = this.config.alternatives?.humanApprovalThreshold || 0.7;
323
+ if (requiresApproval || bestAlternative.confidence < threshold) {
324
+ if (this.config.humanInLoop?.enabled) {
325
+ return this.requestHumanApproval(alternatives, trigger);
326
+ }
327
+ }
328
+
329
+ // Auto-select best alternative
330
+ return {
331
+ type: this.getDecisionType(bestAlternative),
332
+ alternative: bestAlternative,
333
+ reason: `Auto-selected with confidence ${bestAlternative.confidence}`
334
+ };
335
+ }
336
+
337
+ /**
338
+ * Get decision type based on alternative
339
+ * @param {Alternative} alternative - Selected alternative
340
+ * @returns {string} Decision type
341
+ * @private
342
+ */
343
+ getDecisionType(alternative) {
344
+ if (alternative.id === 'retry') {
345
+ return ReplanDecision.RETRY;
346
+ }
347
+ if (alternative.source === 'skip') {
348
+ return ReplanDecision.SKIP;
349
+ }
350
+ return ReplanDecision.REPLACE;
351
+ }
352
+
353
+ /**
354
+ * Request human approval for alternatives
355
+ * @param {Alternative[]} alternatives - Available alternatives
356
+ * @param {Object} trigger - Trigger event
357
+ * @returns {Promise<Decision>} Human decision
358
+ * @private
359
+ */
360
+ async requestHumanApproval(alternatives, trigger) {
361
+ return new Promise((resolve) => {
362
+ const timeout = this.config.humanInLoop?.timeout || 300000;
363
+
364
+ // Emit event for human review
365
+ this.emit('replan:review-required', {
366
+ trigger,
367
+ alternatives,
368
+ timeout,
369
+ respond: (decision) => {
370
+ resolve(decision);
371
+ }
372
+ });
373
+
374
+ // Timeout handling
375
+ setTimeout(() => {
376
+ const defaultAction = this.config.humanInLoop?.defaultOnTimeout || 'abort';
377
+ resolve({
378
+ type: defaultAction === 'abort' ? ReplanDecision.ABORT : ReplanDecision.SKIP,
379
+ alternative: null,
380
+ reason: 'Human approval timeout'
381
+ });
382
+ }, timeout);
383
+ });
384
+ }
385
+
386
+ /**
387
+ * Apply the selected decision
388
+ * @param {Decision} decision - Decision to apply
389
+ * @param {Object} trigger - Original trigger
390
+ * @private
391
+ */
392
+ async applyDecision(decision, trigger) {
393
+ const planId = this.currentPlan.id;
394
+
395
+ switch (decision.type) {
396
+ case ReplanDecision.RETRY:
397
+ // Add task back to pending
398
+ const retryTask = {
399
+ ...decision.alternative.task,
400
+ attempts: (decision.alternative.task.attempts || 0) + 1
401
+ };
402
+ this.executionContext.pending.unshift(retryTask);
403
+ this.executionContext.retries++;
404
+ break;
405
+
406
+ case ReplanDecision.REPLACE:
407
+ // Add alternative task to pending
408
+ this.executionContext.pending.unshift(decision.alternative.task);
409
+ this.planVersion++;
410
+ this.history.recordSnapshot(planId, this.currentPlan, 'Task replaced');
411
+ break;
412
+
413
+ case ReplanDecision.SKIP:
414
+ // Do nothing, task is already removed from pending
415
+ break;
416
+
417
+ case ReplanDecision.INSERT:
418
+ // Insert new tasks at appropriate position
419
+ if (decision.tasks) {
420
+ this.executionContext.pending.unshift(...decision.tasks);
421
+ this.planVersion++;
422
+ }
423
+ break;
424
+
425
+ case ReplanDecision.ABORT:
426
+ // Stop execution
427
+ this.isExecuting = false;
428
+ break;
429
+
430
+ case ReplanDecision.HUMAN_REVIEW:
431
+ // Already handled
432
+ break;
433
+
434
+ default:
435
+ console.warn(`Unknown decision type: ${decision.type}`);
436
+ }
437
+
438
+ // Update history with outcome
439
+ const lastEvent = this.history.getEvents({ limit: 1, sort: 'desc' })[0];
440
+ if (lastEvent) {
441
+ lastEvent.outcome = {
442
+ success: decision.type !== ReplanDecision.ABORT,
443
+ applied: decision.type
444
+ };
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Generate final execution result
450
+ * @returns {ExecutionResult} Final result
451
+ * @private
452
+ */
453
+ generateResult() {
454
+ const evaluation = this.evaluator.evaluate(
455
+ this.currentPlan,
456
+ this.executionContext
457
+ );
458
+
459
+ return {
460
+ planId: this.currentPlan.id,
461
+ planVersion: this.planVersion,
462
+ status: this.executionContext.failed.length === 0 ? 'success' : 'partial',
463
+ completed: this.executionContext.completed,
464
+ failed: this.executionContext.failed,
465
+ pending: this.executionContext.pending,
466
+ evaluation,
467
+ replanCount: this.planVersion,
468
+ history: this.history.getMetrics(),
469
+ duration: Date.now() - this.executionContext.startTime
470
+ };
471
+ }
472
+
473
+ /**
474
+ * Normalize plan structure
475
+ * @param {Object} plan - Input plan
476
+ * @returns {Object} Normalized plan
477
+ * @private
478
+ */
479
+ normalizePlan(plan) {
480
+ const normalizedTasks = (plan.tasks || []).map((task, index) => {
481
+ const normalized = {
482
+ ...task,
483
+ id: task.id || `task-${index}`,
484
+ name: task.name || task.skill,
485
+ skill: task.skill || task.name,
486
+ parameters: task.parameters || {},
487
+ dependencies: task.dependencies || []
488
+ };
489
+ return normalized;
490
+ });
491
+
492
+ return {
493
+ ...plan,
494
+ id: plan.id || `plan-${Date.now()}`,
495
+ version: plan.version || 1,
496
+ tasks: normalizedTasks
497
+ };
498
+ }
499
+
500
+ /**
501
+ * Delegate execution to orchestration engine
502
+ * @param {Object} plan - Plan to execute
503
+ * @param {Object} options - Options
504
+ * @returns {Promise<Object>} Result
505
+ * @private
506
+ */
507
+ async delegateToEngine(plan, options) {
508
+ if (!this.engine) {
509
+ throw new Error('No orchestration engine available');
510
+ }
511
+ return this.engine.execute(plan.pattern || 'sequential', {
512
+ ...options,
513
+ tasks: plan.tasks
514
+ });
515
+ }
516
+
517
+ /**
518
+ * Manually trigger replanning
519
+ * @param {string} [reason] - Reason for manual replan
520
+ * @returns {Promise<void>}
521
+ */
522
+ async replan(reason = 'Manual replan request') {
523
+ if (!this.isExecuting) {
524
+ throw new Error('Cannot replan when not executing');
525
+ }
526
+
527
+ this.monitor.requestReplan(this.currentPlan.id, reason);
528
+ }
529
+
530
+ /**
531
+ * Add a task to the current plan
532
+ * @param {Object} task - Task to add
533
+ * @param {string} [position='end'] - Position: 'start', 'end', or task ID
534
+ */
535
+ addTask(task, position = 'end') {
536
+ if (!this.currentPlan) {
537
+ throw new Error('No active plan');
538
+ }
539
+
540
+ const normalizedTask = {
541
+ id: task.id || `task-${Date.now()}`,
542
+ ...task
543
+ };
544
+
545
+ if (position === 'start') {
546
+ this.executionContext.pending.unshift(normalizedTask);
547
+ } else if (position === 'end') {
548
+ this.executionContext.pending.push(normalizedTask);
549
+ } else {
550
+ // Insert after specific task
551
+ const index = this.executionContext.pending.findIndex(t => t.id === position);
552
+ if (index !== -1) {
553
+ this.executionContext.pending.splice(index + 1, 0, normalizedTask);
554
+ } else {
555
+ this.executionContext.pending.push(normalizedTask);
556
+ }
557
+ }
558
+
559
+ this.planVersion++;
560
+ this.emit('plan:modified', { action: 'add', task: normalizedTask });
561
+ }
562
+
563
+ /**
564
+ * Remove a task from the current plan
565
+ * @param {string} taskId - Task ID to remove
566
+ * @returns {boolean} Whether task was found and removed
567
+ */
568
+ removeTask(taskId) {
569
+ if (!this.currentPlan) {
570
+ throw new Error('No active plan');
571
+ }
572
+
573
+ const index = this.executionContext.pending.findIndex(t => t.id === taskId);
574
+ if (index !== -1) {
575
+ const removed = this.executionContext.pending.splice(index, 1)[0];
576
+ this.planVersion++;
577
+ this.emit('plan:modified', { action: 'remove', task: removed });
578
+ return true;
579
+ }
580
+
581
+ return false;
582
+ }
583
+
584
+ /**
585
+ * Reorder tasks in the current plan
586
+ * @param {string[]} taskIds - Ordered task IDs
587
+ */
588
+ reorderTasks(taskIds) {
589
+ if (!this.currentPlan) {
590
+ throw new Error('No active plan');
591
+ }
592
+
593
+ const taskMap = new Map(
594
+ this.executionContext.pending.map(t => [t.id, t])
595
+ );
596
+
597
+ const reordered = [];
598
+ for (const id of taskIds) {
599
+ const task = taskMap.get(id);
600
+ if (task) {
601
+ reordered.push(task);
602
+ taskMap.delete(id);
603
+ }
604
+ }
605
+
606
+ // Add remaining tasks at end
607
+ reordered.push(...taskMap.values());
608
+
609
+ this.executionContext.pending = reordered;
610
+ this.planVersion++;
611
+ this.emit('plan:modified', { action: 'reorder', order: taskIds });
612
+ }
613
+
614
+ /**
615
+ * Modify a task in the current plan
616
+ * @param {string} taskId - Task ID to modify
617
+ * @param {Object} updates - Updates to apply
618
+ * @returns {boolean} Whether task was found and modified
619
+ */
620
+ modifyTask(taskId, updates) {
621
+ if (!this.currentPlan) {
622
+ throw new Error('No active plan');
623
+ }
624
+
625
+ const task = this.executionContext.pending.find(t => t.id === taskId);
626
+ if (task) {
627
+ Object.assign(task, updates);
628
+ this.planVersion++;
629
+ this.emit('plan:modified', { action: 'modify', taskId, updates });
630
+ return true;
631
+ }
632
+
633
+ return false;
634
+ }
635
+
636
+ /**
637
+ * Get plan history
638
+ * @param {Object} [filter] - Filter options
639
+ * @returns {Object} Plan history
640
+ */
641
+ getPlanHistory(filter) {
642
+ return {
643
+ events: this.history.getEvents(filter),
644
+ metrics: this.history.getMetrics(),
645
+ snapshots: this.currentPlan ? this.history.getSnapshots(this.currentPlan.id) : []
646
+ };
647
+ }
648
+
649
+ /**
650
+ * Get current plan state
651
+ * @returns {Object|null} Current plan
652
+ */
653
+ getCurrentPlan() {
654
+ if (!this.currentPlan) return null;
655
+
656
+ return {
657
+ ...this.currentPlan,
658
+ version: this.planVersion,
659
+ context: this.executionContext
660
+ };
661
+ }
662
+
663
+ /**
664
+ * Check if LLM provider is available
665
+ * @returns {Promise<boolean>}
666
+ */
667
+ async isLLMAvailable() {
668
+ return this.llmProvider.isAvailable();
669
+ }
670
+
671
+ /**
672
+ * Export history report
673
+ * @param {string} [format='markdown'] - Export format
674
+ * @returns {string} Report content
675
+ */
676
+ exportHistory(format = 'markdown') {
677
+ if (format === 'json') {
678
+ return this.history.exportJSON();
679
+ }
680
+ return this.history.exportMarkdown();
681
+ }
682
+ }
683
+
684
+ /**
685
+ * @typedef {Object} ExecutionResult
686
+ * @property {string} planId - Plan identifier
687
+ * @property {number} planVersion - Final plan version
688
+ * @property {string} status - Execution status
689
+ * @property {Object[]} completed - Completed tasks
690
+ * @property {Object[]} failed - Failed tasks
691
+ * @property {Object[]} pending - Remaining pending tasks
692
+ * @property {Object} evaluation - Plan evaluation
693
+ * @property {number} replanCount - Number of replans
694
+ * @property {Object} history - History metrics
695
+ * @property {number} duration - Total duration in ms
696
+ */
697
+
698
+ /**
699
+ * @typedef {Object} Decision
700
+ * @property {string} type - Decision type from ReplanDecision
701
+ * @property {Object|null} alternative - Selected alternative
702
+ * @property {string} reason - Reason for decision
703
+ * @property {Object[]} [tasks] - Tasks for INSERT decision
704
+ */
705
+
706
+ module.exports = { ReplanningEngine };