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,402 @@
1
+ /**
2
+ * @fileoverview Replan History for MUSUBI Replanning Engine
3
+ * Tracks replanning events for audit and learning
4
+ * @module orchestration/replanning/replan-history
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const fs = require('fs').promises;
11
+ const path = require('path');
12
+
13
+ /**
14
+ * Replan History - Tracks and persists replanning events
15
+ */
16
+ class ReplanHistory {
17
+ /**
18
+ * Create a replan history tracker
19
+ * @param {Object} [options={}] - History options
20
+ */
21
+ constructor(options = {}) {
22
+ this.config = {
23
+ enabled: true,
24
+ maxEvents: 1000,
25
+ persist: false,
26
+ filePath: 'storage/replanning-history.json',
27
+ ...options.config
28
+ };
29
+ this.events = [];
30
+ this.snapshots = new Map();
31
+ this.metrics = {
32
+ totalReplans: 0,
33
+ successfulReplans: 0,
34
+ failedReplans: 0,
35
+ byTrigger: {},
36
+ byDecision: {}
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Record a replanning event
42
+ * @param {ReplanEvent} event - Event to record
43
+ */
44
+ record(event) {
45
+ if (!this.config.enabled) return;
46
+
47
+ const enrichedEvent = {
48
+ ...event,
49
+ id: event.id || this.generateEventId(),
50
+ timestamp: event.timestamp || Date.now(),
51
+ version: 1
52
+ };
53
+
54
+ // Add to events list
55
+ this.events.push(enrichedEvent);
56
+
57
+ // Trim if over max
58
+ if (this.events.length > this.config.maxEvents) {
59
+ this.events = this.events.slice(-this.config.maxEvents);
60
+ }
61
+
62
+ // Update metrics
63
+ this.updateMetrics(enrichedEvent);
64
+
65
+ // Persist if configured
66
+ if (this.config.persist) {
67
+ this.persistAsync();
68
+ }
69
+
70
+ return enrichedEvent;
71
+ }
72
+
73
+ /**
74
+ * Record a plan snapshot
75
+ * @param {string} planId - Plan identifier
76
+ * @param {Object} plan - Plan state
77
+ * @param {string} reason - Reason for snapshot
78
+ */
79
+ recordSnapshot(planId, plan, reason) {
80
+ const snapshots = this.snapshots.get(planId) || [];
81
+ snapshots.push({
82
+ timestamp: Date.now(),
83
+ reason,
84
+ plan: JSON.parse(JSON.stringify(plan)), // Deep clone
85
+ version: snapshots.length + 1
86
+ });
87
+ this.snapshots.set(planId, snapshots);
88
+ }
89
+
90
+ /**
91
+ * Get events with optional filtering
92
+ * @param {Object} [filter={}] - Filter options
93
+ * @returns {ReplanEvent[]} Filtered events
94
+ */
95
+ getEvents(filter = {}) {
96
+ let result = [...this.events];
97
+
98
+ // Filter by trigger type
99
+ if (filter.trigger) {
100
+ result = result.filter(e => e.trigger === filter.trigger);
101
+ }
102
+
103
+ // Filter by decision type
104
+ if (filter.decision) {
105
+ result = result.filter(e => e.decision === filter.decision);
106
+ }
107
+
108
+ // Filter by plan ID
109
+ if (filter.planId) {
110
+ result = result.filter(e => e.planId === filter.planId);
111
+ }
112
+
113
+ // Filter by time range
114
+ if (filter.startTime) {
115
+ result = result.filter(e => e.timestamp >= filter.startTime);
116
+ }
117
+ if (filter.endTime) {
118
+ result = result.filter(e => e.timestamp <= filter.endTime);
119
+ }
120
+
121
+ // Filter by success
122
+ if (filter.success !== undefined) {
123
+ result = result.filter(e => e.outcome?.success === filter.success);
124
+ }
125
+
126
+ // Sort
127
+ if (filter.sort === 'desc') {
128
+ result.sort((a, b) => b.timestamp - a.timestamp);
129
+ } else {
130
+ result.sort((a, b) => a.timestamp - b.timestamp);
131
+ }
132
+
133
+ // Limit
134
+ if (filter.limit) {
135
+ result = result.slice(0, filter.limit);
136
+ }
137
+
138
+ return result;
139
+ }
140
+
141
+ /**
142
+ * Get plan snapshots
143
+ * @param {string} planId - Plan identifier
144
+ * @returns {Object[]} Plan snapshots
145
+ */
146
+ getSnapshots(planId) {
147
+ return this.snapshots.get(planId) || [];
148
+ }
149
+
150
+ /**
151
+ * Get metrics summary
152
+ * @returns {Object} Metrics summary
153
+ */
154
+ getMetrics() {
155
+ return {
156
+ ...this.metrics,
157
+ successRate: this.metrics.totalReplans > 0
158
+ ? this.metrics.successfulReplans / this.metrics.totalReplans
159
+ : 0,
160
+ eventCount: this.events.length
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Export history to Markdown format
166
+ * @param {Object} [options={}] - Export options
167
+ * @returns {string} Markdown content
168
+ */
169
+ exportMarkdown(options = {}) {
170
+ const events = this.getEvents(options.filter || {});
171
+ const metrics = this.getMetrics();
172
+
173
+ let md = `# Replanning History Report
174
+
175
+ Generated: ${new Date().toISOString()}
176
+
177
+ ## Summary
178
+
179
+ | Metric | Value |
180
+ |--------|-------|
181
+ | Total Replans | ${metrics.totalReplans} |
182
+ | Successful | ${metrics.successfulReplans} |
183
+ | Failed | ${metrics.failedReplans} |
184
+ | Success Rate | ${(metrics.successRate * 100).toFixed(1)}% |
185
+
186
+ ## Triggers Distribution
187
+
188
+ | Trigger | Count |
189
+ |---------|-------|
190
+ ${Object.entries(metrics.byTrigger).map(([k, v]) => `| ${k} | ${v} |`).join('\n')}
191
+
192
+ ## Decisions Distribution
193
+
194
+ | Decision | Count |
195
+ |----------|-------|
196
+ ${Object.entries(metrics.byDecision).map(([k, v]) => `| ${k} | ${v} |`).join('\n')}
197
+
198
+ ## Events
199
+
200
+ `;
201
+
202
+ for (const event of events) {
203
+ md += `### ${event.id}
204
+
205
+ - **Timestamp**: ${new Date(event.timestamp).toISOString()}
206
+ - **Trigger**: ${event.trigger}
207
+ - **Decision**: ${event.decision}
208
+ - **Plan ID**: ${event.planId || 'N/A'}
209
+ - **Success**: ${event.outcome?.success ? '✅' : '❌'}
210
+
211
+ `;
212
+
213
+ if (event.failedTask) {
214
+ md += `**Failed Task**: ${event.failedTask.name || event.failedTask.skill}
215
+
216
+ `;
217
+ }
218
+
219
+ if (event.selectedAlternative) {
220
+ md += `**Selected Alternative**: ${event.selectedAlternative.description}
221
+ - Confidence: ${event.selectedAlternative.confidence}
222
+ - Reasoning: ${event.selectedAlternative.reasoning}
223
+
224
+ `;
225
+ }
226
+
227
+ if (event.outcome?.error) {
228
+ md += `**Error**: ${event.outcome.error}
229
+
230
+ `;
231
+ }
232
+
233
+ md += `---
234
+
235
+ `;
236
+ }
237
+
238
+ return md;
239
+ }
240
+
241
+ /**
242
+ * Export history to JSON format
243
+ * @param {Object} [options={}] - Export options
244
+ * @returns {string} JSON string
245
+ */
246
+ exportJSON(options = {}) {
247
+ const events = this.getEvents(options.filter || {});
248
+ const metrics = this.getMetrics();
249
+
250
+ return JSON.stringify({
251
+ exportTime: Date.now(),
252
+ metrics,
253
+ events,
254
+ snapshots: Object.fromEntries(this.snapshots)
255
+ }, null, 2);
256
+ }
257
+
258
+ /**
259
+ * Import history from JSON
260
+ * @param {string} json - JSON string
261
+ */
262
+ importJSON(json) {
263
+ try {
264
+ const data = JSON.parse(json);
265
+
266
+ if (data.events) {
267
+ this.events = data.events;
268
+ }
269
+
270
+ if (data.snapshots) {
271
+ this.snapshots = new Map(Object.entries(data.snapshots));
272
+ }
273
+
274
+ // Recalculate metrics
275
+ this.recalculateMetrics();
276
+ } catch (error) {
277
+ throw new Error(`Failed to import history: ${error.message}`);
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Clear all history
283
+ */
284
+ clear() {
285
+ this.events = [];
286
+ this.snapshots.clear();
287
+ this.metrics = {
288
+ totalReplans: 0,
289
+ successfulReplans: 0,
290
+ failedReplans: 0,
291
+ byTrigger: {},
292
+ byDecision: {}
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Update metrics based on event
298
+ * @param {ReplanEvent} event - Event
299
+ * @private
300
+ */
301
+ updateMetrics(event) {
302
+ this.metrics.totalReplans++;
303
+
304
+ if (event.outcome?.success) {
305
+ this.metrics.successfulReplans++;
306
+ } else if (event.outcome?.success === false) {
307
+ this.metrics.failedReplans++;
308
+ }
309
+
310
+ // Track by trigger
311
+ if (event.trigger) {
312
+ this.metrics.byTrigger[event.trigger] =
313
+ (this.metrics.byTrigger[event.trigger] || 0) + 1;
314
+ }
315
+
316
+ // Track by decision
317
+ if (event.decision) {
318
+ this.metrics.byDecision[event.decision] =
319
+ (this.metrics.byDecision[event.decision] || 0) + 1;
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Recalculate metrics from events
325
+ * @private
326
+ */
327
+ recalculateMetrics() {
328
+ this.metrics = {
329
+ totalReplans: 0,
330
+ successfulReplans: 0,
331
+ failedReplans: 0,
332
+ byTrigger: {},
333
+ byDecision: {}
334
+ };
335
+
336
+ for (const event of this.events) {
337
+ this.updateMetrics(event);
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Generate unique event ID
343
+ * @returns {string} Event ID
344
+ * @private
345
+ */
346
+ generateEventId() {
347
+ return `replan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
348
+ }
349
+
350
+ /**
351
+ * Persist history to file asynchronously
352
+ * @private
353
+ */
354
+ async persistAsync() {
355
+ try {
356
+ const filePath = path.resolve(this.config.filePath);
357
+ const dir = path.dirname(filePath);
358
+
359
+ // Ensure directory exists
360
+ await fs.mkdir(dir, { recursive: true });
361
+
362
+ // Write history
363
+ await fs.writeFile(filePath, this.exportJSON(), 'utf8');
364
+ } catch (error) {
365
+ console.error('Failed to persist replan history:', error.message);
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Load history from file
371
+ * @returns {Promise<void>}
372
+ */
373
+ async load() {
374
+ try {
375
+ const filePath = path.resolve(this.config.filePath);
376
+ const data = await fs.readFile(filePath, 'utf8');
377
+ this.importJSON(data);
378
+ } catch (error) {
379
+ if (error.code !== 'ENOENT') {
380
+ console.error('Failed to load replan history:', error.message);
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ /**
387
+ * @typedef {Object} ReplanEvent
388
+ * @property {string} id - Event identifier
389
+ * @property {number} timestamp - Event timestamp
390
+ * @property {string} trigger - Trigger type
391
+ * @property {string} decision - Decision made
392
+ * @property {string} [planId] - Plan identifier
393
+ * @property {Object} [failedTask] - Failed task details
394
+ * @property {Object[]} [alternatives] - Generated alternatives
395
+ * @property {Object} [selectedAlternative] - Selected alternative
396
+ * @property {Object} [outcome] - Outcome of replanning
397
+ * @property {boolean} [outcome.success] - Whether replanning succeeded
398
+ * @property {string} [outcome.error] - Error message if failed
399
+ * @property {Object} [context] - Additional context
400
+ */
401
+
402
+ module.exports = { ReplanHistory };