@chllming/wave-orchestration 0.5.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 (68) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +549 -0
  3. package/docs/agents/wave-deploy-verifier-role.md +34 -0
  4. package/docs/agents/wave-documentation-role.md +30 -0
  5. package/docs/agents/wave-evaluator-role.md +43 -0
  6. package/docs/agents/wave-infra-role.md +34 -0
  7. package/docs/agents/wave-integration-role.md +32 -0
  8. package/docs/agents/wave-launcher-role.md +37 -0
  9. package/docs/context7/bundles.json +91 -0
  10. package/docs/plans/component-cutover-matrix.json +112 -0
  11. package/docs/plans/component-cutover-matrix.md +49 -0
  12. package/docs/plans/context7-wave-orchestrator.md +130 -0
  13. package/docs/plans/current-state.md +44 -0
  14. package/docs/plans/master-plan.md +16 -0
  15. package/docs/plans/migration.md +23 -0
  16. package/docs/plans/wave-orchestrator.md +254 -0
  17. package/docs/plans/waves/wave-0.md +165 -0
  18. package/docs/reference/github-packages-setup.md +52 -0
  19. package/docs/reference/migration-0.2-to-0.5.md +622 -0
  20. package/docs/reference/npmjs-trusted-publishing.md +55 -0
  21. package/docs/reference/repository-guidance.md +18 -0
  22. package/docs/reference/runtime-config/README.md +85 -0
  23. package/docs/reference/runtime-config/claude.md +105 -0
  24. package/docs/reference/runtime-config/codex.md +81 -0
  25. package/docs/reference/runtime-config/opencode.md +93 -0
  26. package/docs/research/agent-context-sources.md +57 -0
  27. package/docs/roadmap.md +626 -0
  28. package/package.json +53 -0
  29. package/releases/manifest.json +101 -0
  30. package/scripts/context7-api-check.sh +21 -0
  31. package/scripts/context7-export-env.sh +52 -0
  32. package/scripts/research/agent-context-archive.mjs +472 -0
  33. package/scripts/research/generate-agent-context-indexes.mjs +85 -0
  34. package/scripts/research/import-agent-context-archive.mjs +793 -0
  35. package/scripts/research/manifests/harness-and-blackboard-2026-03-21.mjs +201 -0
  36. package/scripts/wave-autonomous.mjs +13 -0
  37. package/scripts/wave-cli-bootstrap.mjs +27 -0
  38. package/scripts/wave-dashboard.mjs +11 -0
  39. package/scripts/wave-human-feedback.mjs +11 -0
  40. package/scripts/wave-launcher.mjs +11 -0
  41. package/scripts/wave-local-executor.mjs +13 -0
  42. package/scripts/wave-orchestrator/agent-state.mjs +416 -0
  43. package/scripts/wave-orchestrator/autonomous.mjs +367 -0
  44. package/scripts/wave-orchestrator/clarification-triage.mjs +605 -0
  45. package/scripts/wave-orchestrator/config.mjs +848 -0
  46. package/scripts/wave-orchestrator/context7.mjs +464 -0
  47. package/scripts/wave-orchestrator/coord-cli.mjs +286 -0
  48. package/scripts/wave-orchestrator/coordination-store.mjs +987 -0
  49. package/scripts/wave-orchestrator/coordination.mjs +768 -0
  50. package/scripts/wave-orchestrator/dashboard-renderer.mjs +254 -0
  51. package/scripts/wave-orchestrator/dashboard-state.mjs +473 -0
  52. package/scripts/wave-orchestrator/dep-cli.mjs +219 -0
  53. package/scripts/wave-orchestrator/docs-queue.mjs +75 -0
  54. package/scripts/wave-orchestrator/executors.mjs +385 -0
  55. package/scripts/wave-orchestrator/feedback.mjs +372 -0
  56. package/scripts/wave-orchestrator/install.mjs +540 -0
  57. package/scripts/wave-orchestrator/launcher.mjs +3879 -0
  58. package/scripts/wave-orchestrator/ledger.mjs +332 -0
  59. package/scripts/wave-orchestrator/local-executor.mjs +263 -0
  60. package/scripts/wave-orchestrator/replay.mjs +246 -0
  61. package/scripts/wave-orchestrator/roots.mjs +10 -0
  62. package/scripts/wave-orchestrator/routing-state.mjs +542 -0
  63. package/scripts/wave-orchestrator/shared.mjs +405 -0
  64. package/scripts/wave-orchestrator/terminals.mjs +209 -0
  65. package/scripts/wave-orchestrator/traces.mjs +1094 -0
  66. package/scripts/wave-orchestrator/wave-files.mjs +1923 -0
  67. package/scripts/wave.mjs +103 -0
  68. package/wave.config.json +115 -0
@@ -0,0 +1,464 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ REPO_ROOT,
5
+ compactSingleLine,
6
+ ensureDirectory,
7
+ hashText,
8
+ readJsonOrNull,
9
+ sleep,
10
+ writeJsonAtomic,
11
+ } from "./shared.mjs";
12
+
13
+ export const DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH = path.join(
14
+ REPO_ROOT,
15
+ "docs",
16
+ "context7",
17
+ "bundles.json",
18
+ );
19
+ export const DEFAULT_CONTEXT7_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
20
+ export const DEFAULT_CONTEXT7_PROMPT_CHAR_BUDGET = 12000;
21
+
22
+ const CONTEXT7_SEARCH_URL = "https://context7.com/api/v2/libs/search";
23
+ const CONTEXT7_CONTEXT_URL = "https://context7.com/api/v2/context";
24
+
25
+ function cleanText(value) {
26
+ const text = String(value || "").trim();
27
+ if (!text) {
28
+ return "";
29
+ }
30
+ const wrappedQuote =
31
+ (text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"));
32
+ if (wrappedQuote) {
33
+ return text.slice(1, -1).trim();
34
+ }
35
+ return text;
36
+ }
37
+
38
+ export function normalizeContext7Config(raw) {
39
+ if (!raw || typeof raw !== "object") {
40
+ return null;
41
+ }
42
+ const bundle = cleanText(raw.bundle);
43
+ const query = compactSingleLine(cleanText(raw.query), 280);
44
+ if (!bundle && !query) {
45
+ return null;
46
+ }
47
+ return {
48
+ bundle: bundle || null,
49
+ query: query || null,
50
+ };
51
+ }
52
+
53
+ function normalizeBundleLibrary(entry, bundleId) {
54
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
55
+ throw new Error(`Context7 bundle "${bundleId}" has a malformed library entry.`);
56
+ }
57
+ const libraryId = cleanText(entry.libraryId);
58
+ const libraryName = cleanText(entry.libraryName);
59
+ if (!libraryId && !libraryName) {
60
+ throw new Error(
61
+ `Context7 bundle "${bundleId}" must define either "libraryId" or "libraryName" for each library.`,
62
+ );
63
+ }
64
+ return {
65
+ libraryId: libraryId || null,
66
+ libraryName: libraryName || null,
67
+ queryHint: compactSingleLine(cleanText(entry.queryHint), 220) || null,
68
+ };
69
+ }
70
+
71
+ function normalizeBundleDefinition(bundleId, rawBundle) {
72
+ if (!rawBundle || typeof rawBundle !== "object" || Array.isArray(rawBundle)) {
73
+ throw new Error(`Context7 bundle "${bundleId}" must be an object.`);
74
+ }
75
+ const libraries = Array.isArray(rawBundle.libraries)
76
+ ? rawBundle.libraries.map((entry) => normalizeBundleLibrary(entry, bundleId))
77
+ : [];
78
+ return {
79
+ description: compactSingleLine(cleanText(rawBundle.description), 220) || "",
80
+ libraries,
81
+ };
82
+ }
83
+
84
+ export function loadContext7BundleIndex(indexPath = DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH) {
85
+ const payload = readJsonOrNull(indexPath);
86
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
87
+ throw new Error(
88
+ `Context7 bundle index is missing or invalid: ${path.relative(REPO_ROOT, indexPath)}`,
89
+ );
90
+ }
91
+ const rawBundles = payload.bundles;
92
+ if (!rawBundles || typeof rawBundles !== "object" || Array.isArray(rawBundles)) {
93
+ throw new Error(`Context7 bundle index must define a "bundles" object.`);
94
+ }
95
+ const bundles = Object.fromEntries(
96
+ Object.entries(rawBundles).map(([bundleId, rawBundle]) => [
97
+ bundleId,
98
+ normalizeBundleDefinition(bundleId, rawBundle),
99
+ ]),
100
+ );
101
+ const defaultBundle = cleanText(payload.defaultBundle) || "none";
102
+ if (!bundles[defaultBundle]) {
103
+ throw new Error(`Context7 default bundle "${defaultBundle}" is not defined.`);
104
+ }
105
+ if (!bundles.none) {
106
+ throw new Error('Context7 bundle index must define a "none" bundle.');
107
+ }
108
+ const laneDefaults = Object.fromEntries(
109
+ Object.entries(payload.laneDefaults || {})
110
+ .map(([lane, bundleId]) => [String(lane || "").trim().toLowerCase(), cleanText(bundleId)])
111
+ .filter(([, bundleId]) => Boolean(bundleId)),
112
+ );
113
+ for (const [lane, bundleId] of Object.entries(laneDefaults)) {
114
+ if (!bundles[bundleId]) {
115
+ throw new Error(
116
+ `Context7 lane default "${lane}" references unknown bundle "${bundleId}".`,
117
+ );
118
+ }
119
+ }
120
+ const canonicalPayload = {
121
+ version: Number.parseInt(String(payload.version ?? "1"), 10) || 1,
122
+ defaultBundle,
123
+ laneDefaults,
124
+ bundles,
125
+ };
126
+ return {
127
+ ...canonicalPayload,
128
+ indexPath,
129
+ contentHash: hashText(JSON.stringify(canonicalPayload)),
130
+ };
131
+ }
132
+
133
+ function deriveContext7Query(agent) {
134
+ const lines = String(agent?.promptOverlay || agent?.prompt || "")
135
+ .split(/\r?\n/)
136
+ .map((line) => line.trim())
137
+ .filter(Boolean)
138
+ .filter(
139
+ (line) =>
140
+ !/^File ownership\b/i.test(line) &&
141
+ !/^Validation\b/i.test(line) &&
142
+ !/^Output\b/i.test(line),
143
+ )
144
+ .slice(0, 6);
145
+ return compactSingleLine(lines.join(" "), 260) || compactSingleLine(agent?.title || "", 120) || "";
146
+ }
147
+
148
+ export function resolveContext7Selection({ lane, waveDefaults, agentConfig, agent, bundleIndex }) {
149
+ const normalizedWaveDefaults = normalizeContext7Config(waveDefaults);
150
+ const normalizedAgentConfig = normalizeContext7Config(agentConfig);
151
+ const laneKey = String(lane || "").trim().toLowerCase();
152
+ const laneDefaultBundle = bundleIndex.laneDefaults[laneKey] || bundleIndex.defaultBundle || "none";
153
+ const bundleId =
154
+ normalizedAgentConfig?.bundle ||
155
+ normalizedWaveDefaults?.bundle ||
156
+ laneDefaultBundle ||
157
+ "none";
158
+ const bundle = bundleIndex.bundles[bundleId];
159
+ if (!bundle) {
160
+ throw new Error(`Unknown Context7 bundle "${bundleId}" for agent ${agent?.agentId || "unknown"}.`);
161
+ }
162
+ let query = "";
163
+ let querySource = "none";
164
+ if (bundleId !== "none") {
165
+ if (normalizedAgentConfig?.query) {
166
+ query = normalizedAgentConfig.query;
167
+ querySource = "agent";
168
+ } else if (normalizedWaveDefaults?.query) {
169
+ query = normalizedWaveDefaults.query;
170
+ querySource = "wave";
171
+ } else {
172
+ query = deriveContext7Query(agent);
173
+ querySource = query ? "derived" : "none";
174
+ }
175
+ }
176
+ const selection = {
177
+ bundleId,
178
+ description: bundle.description || "",
179
+ libraries: bundle.libraries,
180
+ query,
181
+ bundleSource: normalizedAgentConfig?.bundle
182
+ ? "agent"
183
+ : normalizedWaveDefaults?.bundle
184
+ ? "wave"
185
+ : bundleIndex.laneDefaults[laneKey]
186
+ ? "lane"
187
+ : "default",
188
+ querySource,
189
+ indexHash: bundleIndex.contentHash,
190
+ };
191
+ return {
192
+ ...selection,
193
+ selectionHash: hashText(JSON.stringify(selection)),
194
+ };
195
+ }
196
+
197
+ export function applyContext7SelectionsToWave(wave, { lane, bundleIndex }) {
198
+ const context7Defaults = normalizeContext7Config(wave.context7Defaults);
199
+ return {
200
+ ...wave,
201
+ context7Defaults,
202
+ agents: wave.agents.map((agent) => ({
203
+ ...agent,
204
+ context7Config: normalizeContext7Config(agent.context7Config),
205
+ context7Resolved: resolveContext7Selection({
206
+ lane,
207
+ waveDefaults: context7Defaults,
208
+ agentConfig: agent.context7Config,
209
+ agent,
210
+ bundleIndex,
211
+ }),
212
+ })),
213
+ };
214
+ }
215
+
216
+ export function buildAgentPromptFingerprintSource(agent) {
217
+ return JSON.stringify({
218
+ prompt: String(agent?.prompt || ""),
219
+ context7SelectionHash: String(agent?.context7Resolved?.selectionHash || ""),
220
+ exitContract: agent?.exitContract || null,
221
+ components: agent?.components || [],
222
+ componentTargets: agent?.componentTargets || null,
223
+ executorResolved: agent?.executorResolved || null,
224
+ });
225
+ }
226
+
227
+ export function hashAgentPromptFingerprint(agent) {
228
+ return hashText(buildAgentPromptFingerprintSource(agent));
229
+ }
230
+
231
+ function context7CacheKey(selection) {
232
+ return hashText(
233
+ JSON.stringify({
234
+ bundleId: selection.bundleId,
235
+ query: selection.query,
236
+ libraries: selection.libraries,
237
+ indexHash: selection.indexHash,
238
+ }),
239
+ );
240
+ }
241
+
242
+ function context7CachePath(cacheDir, selection) {
243
+ return path.join(cacheDir, `${context7CacheKey(selection)}.json`);
244
+ }
245
+
246
+ function trimContextText(text, budget = DEFAULT_CONTEXT7_PROMPT_CHAR_BUDGET) {
247
+ const trimmed = String(text || "").trim();
248
+ if (trimmed.length <= budget) {
249
+ return trimmed;
250
+ }
251
+ const suffix = "\n\n[Context7 output truncated to fit prompt budget]";
252
+ return `${trimmed.slice(0, Math.max(0, budget - suffix.length)).trimEnd()}${suffix}`;
253
+ }
254
+
255
+ function renderPrefetchedContextText({ selection, results, budget }) {
256
+ const sections = results.map((result) => {
257
+ const label = result.libraryName || result.libraryId || "unknown-library";
258
+ return [
259
+ `### ${label}`,
260
+ `- Library ID: ${result.libraryId || "unresolved"}`,
261
+ `- Query: ${result.query}`,
262
+ "",
263
+ result.text.trim(),
264
+ ].join("\n");
265
+ });
266
+ return trimContextText(sections.join("\n\n"), budget);
267
+ }
268
+
269
+ async function requestContext7(fetchImpl, url, { apiKey, expectText = false, maxRetries = 3 } = {}) {
270
+ let lastError = null;
271
+ for (let attempt = 0; attempt < maxRetries; attempt += 1) {
272
+ const response = await fetchImpl(url, {
273
+ method: "GET",
274
+ headers: {
275
+ Authorization: `Bearer ${apiKey}`,
276
+ Accept: expectText ? "text/plain, application/json" : "application/json",
277
+ },
278
+ });
279
+ if (response.ok) {
280
+ return expectText ? response.text() : response.json();
281
+ }
282
+ const retryAfterHeader = response.headers.get("retry-after");
283
+ const retryAfterMs = retryAfterHeader
284
+ ? Math.max(0, Number.parseInt(retryAfterHeader, 10) || 0) * 1000
285
+ : 0;
286
+ let payload = null;
287
+ try {
288
+ payload = await response.json();
289
+ } catch {
290
+ payload = null;
291
+ }
292
+ lastError = new Error(
293
+ `Context7 request failed (${response.status}): ${payload?.message || payload?.error || response.statusText || "unknown error"}`,
294
+ );
295
+ if (![202, 429, 500, 503].includes(response.status) || attempt >= maxRetries - 1) {
296
+ throw lastError;
297
+ }
298
+ await sleep(Math.max(retryAfterMs, 1000 * 2 ** attempt));
299
+ }
300
+ throw lastError || new Error("Context7 request failed.");
301
+ }
302
+
303
+ async function resolveLibraryId(fetchImpl, library, selection, apiKey) {
304
+ if (library.libraryId) {
305
+ return {
306
+ libraryId: library.libraryId,
307
+ libraryName: library.libraryName || library.libraryId,
308
+ };
309
+ }
310
+ const params = new URLSearchParams({
311
+ libraryName: library.libraryName,
312
+ query: selection.query || library.queryHint || library.libraryName,
313
+ });
314
+ const results = await requestContext7(fetchImpl, `${CONTEXT7_SEARCH_URL}?${params.toString()}`, {
315
+ apiKey,
316
+ });
317
+ if (!Array.isArray(results) || results.length === 0) {
318
+ throw new Error(`Context7 search returned no matches for "${library.libraryName}".`);
319
+ }
320
+ return {
321
+ libraryId: cleanText(results[0]?.id),
322
+ libraryName: cleanText(results[0]?.name) || library.libraryName,
323
+ };
324
+ }
325
+
326
+ async function fetchLibraryContext(fetchImpl, library, selection, apiKey) {
327
+ const resolvedLibrary = await resolveLibraryId(fetchImpl, library, selection, apiKey);
328
+ const query = compactSingleLine(
329
+ [selection.query, library.queryHint].filter(Boolean).join(". Focus: "),
330
+ 320,
331
+ );
332
+ const params = new URLSearchParams({
333
+ libraryId: resolvedLibrary.libraryId,
334
+ query,
335
+ type: "txt",
336
+ });
337
+ const text = await requestContext7(fetchImpl, `${CONTEXT7_CONTEXT_URL}?${params.toString()}`, {
338
+ apiKey,
339
+ expectText: true,
340
+ });
341
+ return {
342
+ libraryId: resolvedLibrary.libraryId,
343
+ libraryName: resolvedLibrary.libraryName,
344
+ query,
345
+ text: String(text || "").trim(),
346
+ };
347
+ }
348
+
349
+ export async function prefetchContext7ForSelection(
350
+ selection,
351
+ {
352
+ cacheDir,
353
+ apiKey = process.env.CONTEXT7_API_KEY || "",
354
+ fetchImpl = globalThis.fetch,
355
+ disabled = false,
356
+ budget = DEFAULT_CONTEXT7_PROMPT_CHAR_BUDGET,
357
+ ttlMs = DEFAULT_CONTEXT7_CACHE_TTL_MS,
358
+ nowMs = Date.now(),
359
+ } = {},
360
+ ) {
361
+ if (!selection || selection.bundleId === "none" || selection.libraries.length === 0) {
362
+ return {
363
+ mode: "none",
364
+ selection,
365
+ promptText: "",
366
+ snippetHash: "",
367
+ warning: "",
368
+ };
369
+ }
370
+ if (disabled) {
371
+ return {
372
+ mode: "disabled",
373
+ selection,
374
+ promptText: "",
375
+ snippetHash: "",
376
+ warning: "Context7 prefetch disabled for this launcher run.",
377
+ };
378
+ }
379
+ if (typeof fetchImpl !== "function") {
380
+ return {
381
+ mode: "unavailable",
382
+ selection,
383
+ promptText: "",
384
+ snippetHash: "",
385
+ warning: "Context7 fetch is unavailable in this Node runtime.",
386
+ };
387
+ }
388
+ if (!apiKey) {
389
+ return {
390
+ mode: "missing-key",
391
+ selection,
392
+ promptText: "",
393
+ snippetHash: "",
394
+ warning: "CONTEXT7_API_KEY is not set; skipping Context7 prefetch.",
395
+ };
396
+ }
397
+
398
+ ensureDirectory(cacheDir);
399
+ const cachePath = context7CachePath(cacheDir, selection);
400
+ const cached = readJsonOrNull(cachePath);
401
+ const cachedAtMs = Date.parse(String(cached?.createdAt || ""));
402
+ if (
403
+ cached &&
404
+ typeof cached === "object" &&
405
+ Number.isFinite(cachedAtMs) &&
406
+ nowMs - cachedAtMs <= ttlMs &&
407
+ typeof cached.promptText === "string"
408
+ ) {
409
+ return {
410
+ mode: "cached",
411
+ selection,
412
+ promptText: cached.promptText,
413
+ snippetHash: String(cached.snippetHash || ""),
414
+ warning: "",
415
+ };
416
+ }
417
+
418
+ try {
419
+ const results = [];
420
+ for (const library of selection.libraries) {
421
+ const result = await fetchLibraryContext(fetchImpl, library, selection, apiKey);
422
+ if (result.text) {
423
+ results.push(result);
424
+ }
425
+ }
426
+ if (results.length === 0) {
427
+ return {
428
+ mode: "empty",
429
+ selection,
430
+ promptText: "",
431
+ snippetHash: "",
432
+ warning: `Context7 returned no promptable snippets for bundle "${selection.bundleId}".`,
433
+ };
434
+ }
435
+ const promptText = renderPrefetchedContextText({ selection, results, budget });
436
+ const snippetHash = hashText(promptText);
437
+ writeJsonAtomic(cachePath, {
438
+ createdAt: new Date(nowMs).toISOString(),
439
+ promptText,
440
+ snippetHash,
441
+ });
442
+ return {
443
+ mode: "fetched",
444
+ selection,
445
+ promptText,
446
+ snippetHash,
447
+ warning: "",
448
+ };
449
+ } catch (error) {
450
+ return {
451
+ mode: "error",
452
+ selection,
453
+ promptText: "",
454
+ snippetHash: "",
455
+ warning: error instanceof Error ? error.message : String(error),
456
+ };
457
+ }
458
+ }
459
+
460
+ export function describeContext7Libraries(selection) {
461
+ return (selection?.libraries || [])
462
+ .map((library) => library.libraryName || library.libraryId || "unknown-library")
463
+ .join(", ");
464
+ }