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.
- package/README.md +25 -3
- package/bin/musubi-orchestrate.js +309 -0
- package/package.json +1 -1
- package/src/llm-providers/anthropic-provider.js +175 -0
- package/src/llm-providers/base-provider.js +221 -0
- package/src/llm-providers/copilot-provider.js +262 -0
- package/src/llm-providers/index.js +214 -0
- package/src/llm-providers/openai-provider.js +205 -0
- package/src/orchestration/index.js +25 -0
- package/src/orchestration/patterns/swarm.js +111 -4
- package/src/orchestration/replanning/adaptive-goal-modifier.js +1150 -0
- package/src/orchestration/replanning/alternative-generator.js +508 -0
- package/src/orchestration/replanning/config.js +378 -0
- package/src/orchestration/replanning/goal-progress-tracker.js +727 -0
- package/src/orchestration/replanning/index.js +82 -0
- package/src/orchestration/replanning/plan-evaluator.js +455 -0
- package/src/orchestration/replanning/plan-monitor.js +379 -0
- package/src/orchestration/replanning/proactive-path-optimizer.js +972 -0
- package/src/orchestration/replanning/replan-history.js +402 -0
- package/src/orchestration/replanning/replanning-engine.js +706 -0
- package/src/templates/agents/claude-code/CLAUDE.md +45 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +20 -0
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +89 -0
- package/src/templates/agents/codex/AGENTS.md +13 -0
- package/src/templates/agents/cursor/AGENTS.md +13 -0
- package/src/templates/agents/gemini-cli/GEMINI.md +13 -0
- package/src/templates/agents/github-copilot/AGENTS.md +13 -0
- package/src/templates/agents/qwen-code/QWEN.md +13 -0
- 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 };
|