@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,367 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { DEFAULT_EXECUTOR_MODE, normalizeExecutorMode, SUPPORTED_EXECUTOR_MODES } from "./config.mjs";
5
+ import {
6
+ DEFAULT_AGENT_LAUNCH_STAGGER_MS,
7
+ DEFAULT_AGENT_RATE_LIMIT_BASE_DELAY_SECONDS,
8
+ DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS,
9
+ DEFAULT_AGENT_RATE_LIMIT_RETRIES,
10
+ DEFAULT_MAX_RETRIES_PER_WAVE,
11
+ DEFAULT_TIMEOUT_MINUTES,
12
+ DEFAULT_WAVE_LANE,
13
+ PACKAGE_ROOT,
14
+ REPO_ROOT,
15
+ buildLanePaths,
16
+ parseNonNegativeInt,
17
+ parsePositiveInt,
18
+ sanitizeLaneName,
19
+ } from "./shared.mjs";
20
+ import {
21
+ DEFAULT_CODEX_SANDBOX_MODE,
22
+ normalizeCodexSandboxMode,
23
+ } from "./launcher.mjs";
24
+ import { readRunState } from "./wave-files.mjs";
25
+ import { readDependencyTickets } from "./coordination-store.mjs";
26
+ import { readWaveLedger } from "./ledger.mjs";
27
+
28
+ function printUsage() {
29
+ console.log(`Usage: pnpm exec wave autonomous [options]
30
+
31
+ Options:
32
+ --lane <name> Lane name (default: ${DEFAULT_WAVE_LANE})
33
+ --timeout-minutes <n> Per-wave timeout passed to launcher (default: ${DEFAULT_TIMEOUT_MINUTES})
34
+ --max-retries-per-wave <n> Per-wave relaunches inside launcher (default: ${DEFAULT_MAX_RETRIES_PER_WAVE})
35
+ --max-attempts-per-wave <n> External attempts for each wave (default: 1)
36
+ --agent-rate-limit-retries <n>
37
+ Per-agent retries for 429 or rate-limit errors (default: ${DEFAULT_AGENT_RATE_LIMIT_RETRIES})
38
+ --agent-rate-limit-base-delay-seconds <n>
39
+ Base backoff delay for 429 retries (default: ${DEFAULT_AGENT_RATE_LIMIT_BASE_DELAY_SECONDS})
40
+ --agent-rate-limit-max-delay-seconds <n>
41
+ Max backoff delay for 429 retries (default: ${DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS})
42
+ --agent-launch-stagger-ms <n> Delay between agent launches (default: ${DEFAULT_AGENT_LAUNCH_STAGGER_MS})
43
+ --orchestrator-id <id> Orchestrator ID for coordination board
44
+ --executor <mode> Default executor passed to launcher: ${SUPPORTED_EXECUTOR_MODES.join(" | ")} (default: lane config)
45
+ --codex-sandbox <mode> Default Codex sandbox mode passed to launcher (default: ${DEFAULT_CODEX_SANDBOX_MODE})
46
+ --dashboard Enable dashboards (default: disabled)
47
+ --keep-sessions Keep tmux sessions between waves
48
+ --keep-terminals Keep temporary terminal entries between waves
49
+ `);
50
+ }
51
+
52
+ export function parseArgs(argv) {
53
+ const options = {
54
+ lane: DEFAULT_WAVE_LANE,
55
+ timeoutMinutes: DEFAULT_TIMEOUT_MINUTES,
56
+ maxRetriesPerWave: DEFAULT_MAX_RETRIES_PER_WAVE,
57
+ maxAttemptsPerWave: 1,
58
+ agentRateLimitRetries: DEFAULT_AGENT_RATE_LIMIT_RETRIES,
59
+ agentRateLimitBaseDelaySeconds: DEFAULT_AGENT_RATE_LIMIT_BASE_DELAY_SECONDS,
60
+ agentRateLimitMaxDelaySeconds: DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS,
61
+ agentLaunchStaggerMs: DEFAULT_AGENT_LAUNCH_STAGGER_MS,
62
+ orchestratorId: null,
63
+ executorMode: DEFAULT_EXECUTOR_MODE,
64
+ codexSandboxMode: DEFAULT_CODEX_SANDBOX_MODE,
65
+ noDashboard: true,
66
+ keepSessions: false,
67
+ keepTerminals: false,
68
+ };
69
+ let executorProvided = false;
70
+ for (let i = 0; i < argv.length; i += 1) {
71
+ const arg = argv[i];
72
+ if (arg === "--") {
73
+ continue;
74
+ }
75
+ if (arg === "--help" || arg === "-h") {
76
+ return { help: true, options };
77
+ }
78
+ if (arg === "--lane") {
79
+ options.lane = sanitizeLaneName(argv[++i]);
80
+ } else if (arg === "--timeout-minutes") {
81
+ options.timeoutMinutes = parsePositiveInt(argv[++i], "--timeout-minutes");
82
+ } else if (arg === "--max-retries-per-wave") {
83
+ options.maxRetriesPerWave = parseNonNegativeInt(argv[++i], "--max-retries-per-wave");
84
+ } else if (arg === "--max-attempts-per-wave") {
85
+ options.maxAttemptsPerWave = parsePositiveInt(argv[++i], "--max-attempts-per-wave");
86
+ } else if (arg === "--agent-rate-limit-retries") {
87
+ options.agentRateLimitRetries = parseNonNegativeInt(argv[++i], "--agent-rate-limit-retries");
88
+ } else if (arg === "--agent-rate-limit-base-delay-seconds") {
89
+ options.agentRateLimitBaseDelaySeconds = parsePositiveInt(
90
+ argv[++i],
91
+ "--agent-rate-limit-base-delay-seconds",
92
+ );
93
+ } else if (arg === "--agent-rate-limit-max-delay-seconds") {
94
+ options.agentRateLimitMaxDelaySeconds = parsePositiveInt(
95
+ argv[++i],
96
+ "--agent-rate-limit-max-delay-seconds",
97
+ );
98
+ } else if (arg === "--agent-launch-stagger-ms") {
99
+ options.agentLaunchStaggerMs = parseNonNegativeInt(argv[++i], "--agent-launch-stagger-ms");
100
+ } else if (arg === "--orchestrator-id") {
101
+ options.orchestratorId = String(argv[++i] || "").trim();
102
+ } else if (arg === "--executor") {
103
+ options.executorMode = normalizeExecutorMode(argv[++i], "--executor");
104
+ executorProvided = true;
105
+ } else if (arg === "--codex-sandbox") {
106
+ options.codexSandboxMode = normalizeCodexSandboxMode(argv[++i], "--codex-sandbox");
107
+ } else if (arg === "--dashboard") {
108
+ options.noDashboard = false;
109
+ } else if (arg === "--keep-sessions") {
110
+ options.keepSessions = true;
111
+ } else if (arg === "--keep-terminals") {
112
+ options.keepTerminals = true;
113
+ } else {
114
+ throw new Error(`Unknown argument: ${arg}`);
115
+ }
116
+ }
117
+ if (!executorProvided) {
118
+ options.executorMode = buildLanePaths(options.lane).executors.default;
119
+ }
120
+ options.orchestratorId ||= `${options.lane}-autonomous`;
121
+ if (options.executorMode === "local") {
122
+ throw new Error("Autonomous mode does not support --executor local. Use codex, claude, or opencode.");
123
+ }
124
+ if (options.agentRateLimitMaxDelaySeconds < options.agentRateLimitBaseDelaySeconds) {
125
+ throw new Error(
126
+ "--agent-rate-limit-max-delay-seconds must be >= --agent-rate-limit-base-delay-seconds",
127
+ );
128
+ }
129
+ return { help: false, options };
130
+ }
131
+
132
+ function getWaveNumbers(lane) {
133
+ const lanePaths = buildLanePaths(lane);
134
+ if (!fs.existsSync(lanePaths.wavesDir)) {
135
+ throw new Error(`Waves directory not found: ${path.relative(REPO_ROOT, lanePaths.wavesDir)}`);
136
+ }
137
+ const waveNumbers = fs
138
+ .readdirSync(lanePaths.wavesDir)
139
+ .filter((name) => /^wave-\d+\.md$/.test(name))
140
+ .map((name) => Number.parseInt(name.match(/^wave-(\d+)\.md$/)[1], 10))
141
+ .toSorted((a, b) => a - b);
142
+ if (waveNumbers.length === 0) {
143
+ throw new Error(`No wave files found in ${path.relative(REPO_ROOT, lanePaths.wavesDir)}`);
144
+ }
145
+ return waveNumbers;
146
+ }
147
+
148
+ export function nextIncompleteWave(allWaves, completed) {
149
+ const done = new Set(completed);
150
+ for (const wave of allWaves) {
151
+ if (!done.has(wave)) {
152
+ return wave;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+
158
+ function runCommand(args) {
159
+ const result = spawnSync("node", args, {
160
+ cwd: REPO_ROOT,
161
+ stdio: "inherit",
162
+ env: process.env,
163
+ });
164
+ return Number.isInteger(result.status) ? result.status : 1;
165
+ }
166
+
167
+ function reconcile(lane) {
168
+ return runCommand([path.join(PACKAGE_ROOT, "scripts", "wave-launcher.mjs"), "--lane", lane, "--reconcile-status"]);
169
+ }
170
+
171
+ function dryRun(lane) {
172
+ return runCommand([path.join(PACKAGE_ROOT, "scripts", "wave-launcher.mjs"), "--lane", lane, "--dry-run", "--no-dashboard"]);
173
+ }
174
+
175
+ function listPendingFeedback(lane) {
176
+ return runCommand([path.join(PACKAGE_ROOT, "scripts", "wave-human-feedback.mjs"), "list", "--lane", lane, "--pending"]);
177
+ }
178
+
179
+ function launchSingleWave(params) {
180
+ const args = [
181
+ path.join(PACKAGE_ROOT, "scripts", "wave-launcher.mjs"),
182
+ "--lane",
183
+ params.lane,
184
+ "--start-wave",
185
+ String(params.wave),
186
+ "--end-wave",
187
+ String(params.wave),
188
+ "--timeout-minutes",
189
+ String(params.timeoutMinutes),
190
+ "--max-retries-per-wave",
191
+ String(params.maxRetriesPerWave),
192
+ "--agent-rate-limit-retries",
193
+ String(params.agentRateLimitRetries),
194
+ "--agent-rate-limit-base-delay-seconds",
195
+ String(params.agentRateLimitBaseDelaySeconds),
196
+ "--agent-rate-limit-max-delay-seconds",
197
+ String(params.agentRateLimitMaxDelaySeconds),
198
+ "--agent-launch-stagger-ms",
199
+ String(params.agentLaunchStaggerMs),
200
+ "--executor",
201
+ params.executorMode,
202
+ "--codex-sandbox",
203
+ params.codexSandboxMode,
204
+ "--orchestrator-id",
205
+ params.orchestratorId,
206
+ "--coordination-note",
207
+ `autonomous single-wave run wave=${params.wave} attempt=${params.attempt}`,
208
+ ];
209
+ if (params.noDashboard) {
210
+ args.push("--no-dashboard");
211
+ }
212
+ if (params.keepSessions) {
213
+ args.push("--keep-sessions");
214
+ }
215
+ if (params.keepTerminals) {
216
+ args.push("--keep-terminals");
217
+ }
218
+ return runCommand(args);
219
+ }
220
+
221
+ function requiredInboundDependenciesOpen(lanePaths, lane) {
222
+ return readDependencyTickets(lanePaths.crossLaneDependenciesDir, lane).filter((record) => {
223
+ const required = record.required === true || String(record.closureCondition || "").includes("required=true");
224
+ return required && ["open", "acknowledged", "in_progress"].includes(record.status);
225
+ });
226
+ }
227
+
228
+ function pendingHumanItemsForWave(lanePaths, wave) {
229
+ const existingLedger = readWaveLedger(path.join(lanePaths.ledgerDir, `wave-${wave}.json`));
230
+ return [
231
+ ...(existingLedger?.humanFeedback || []),
232
+ ...(existingLedger?.humanEscalations || []),
233
+ ];
234
+ }
235
+
236
+ function pendingHumanItemsForLane(lanePaths) {
237
+ if (!fs.existsSync(lanePaths.ledgerDir)) {
238
+ return [];
239
+ }
240
+ return fs
241
+ .readdirSync(lanePaths.ledgerDir, { withFileTypes: true })
242
+ .filter((entry) => entry.isFile() && /^wave-\d+\.json$/i.test(entry.name))
243
+ .map((entry) => ({
244
+ entry,
245
+ wave: Number.parseInt(entry.name.replace(/^wave-|\.json$/gi, ""), 10),
246
+ }))
247
+ .filter((item) => Number.isFinite(item.wave))
248
+ .sort((left, right) => left.wave - right.wave)
249
+ .flatMap((item) =>
250
+ pendingHumanItemsForWave(lanePaths, item.wave).map((id) => ({
251
+ wave: item.wave,
252
+ id,
253
+ })),
254
+ );
255
+ }
256
+
257
+ export function readAutonomousBarrier(lanePaths, lane, wave = null) {
258
+ const dependencyBlockers = requiredInboundDependenciesOpen(lanePaths, lane);
259
+ if (dependencyBlockers.length > 0) {
260
+ return {
261
+ kind: "dependencies",
262
+ dependencyBlockers,
263
+ pendingHumanItems: [],
264
+ message:
265
+ wave === null
266
+ ? `Stopping finalization for lane ${lane}: unresolved required inbound dependencies remain (${dependencyBlockers.map((item) => item.id).join(", ")}).`
267
+ : `Stopping before wave ${wave}: unresolved required inbound dependencies remain (${dependencyBlockers.map((item) => item.id).join(", ")}).`,
268
+ };
269
+ }
270
+ if (wave === null) {
271
+ const pendingHumanEntries = pendingHumanItemsForLane(lanePaths);
272
+ if (pendingHumanEntries.length > 0) {
273
+ return {
274
+ kind: "human-input",
275
+ dependencyBlockers: [],
276
+ pendingHumanItems: pendingHumanEntries.map((entry) => entry.id),
277
+ message: `Stopping finalization for lane ${lane}: pending human input remains (${pendingHumanEntries.map((entry) => `wave ${entry.wave}: ${entry.id}`).join(", ")}).`,
278
+ };
279
+ }
280
+ return null;
281
+ }
282
+ const pendingHumanItems = pendingHumanItemsForWave(lanePaths, wave);
283
+ if (pendingHumanItems.length > 0) {
284
+ return {
285
+ kind: "human-input",
286
+ dependencyBlockers: [],
287
+ pendingHumanItems,
288
+ message: `Stopping before wave ${wave}: pending human input remains in the ledger (${pendingHumanItems.join(", ")}).`,
289
+ };
290
+ }
291
+ return null;
292
+ }
293
+
294
+ export function runAutonomousCli(argv) {
295
+ const parsed = parseArgs(argv);
296
+ if (parsed.help) {
297
+ printUsage();
298
+ return;
299
+ }
300
+ const options = parsed.options;
301
+ const allWaves = getWaveNumbers(options.lane);
302
+ console.log(`[autonomous] lane=${options.lane} orchestrator=${options.orchestratorId}`);
303
+ console.log(`[autonomous] executor=${options.executorMode}`);
304
+ console.log(`[autonomous] codex_sandbox=${options.codexSandboxMode}`);
305
+ console.log(`[autonomous] waves=${allWaves.join(", ")}`);
306
+
307
+ const dryRunStatus = dryRun(options.lane);
308
+ if (dryRunStatus !== 0) {
309
+ throw new Error(`[autonomous] dry-run preflight failed with status=${dryRunStatus}`);
310
+ }
311
+ const feedbackListStatus = listPendingFeedback(options.lane);
312
+ if (feedbackListStatus !== 0) {
313
+ throw new Error(`[autonomous] feedback preflight failed with status=${feedbackListStatus}`);
314
+ }
315
+ const reconcileStatus = reconcile(options.lane);
316
+ if (reconcileStatus !== 0) {
317
+ throw new Error(`[autonomous] initial reconcile failed with status=${reconcileStatus}`);
318
+ }
319
+
320
+ let launchedCount = 0;
321
+ const lanePaths = buildLanePaths(options.lane);
322
+ while (true) {
323
+ const completed = readRunState(lanePaths.defaultRunStatePath).completedWaves;
324
+ const wave = nextIncompleteWave(allWaves, completed);
325
+ if (wave === null) {
326
+ const finalBarrier = readAutonomousBarrier(lanePaths, options.lane);
327
+ if (finalBarrier) {
328
+ throw new Error(finalBarrier.message);
329
+ }
330
+ console.log(`[autonomous] all waves complete for lane=${options.lane}`);
331
+ break;
332
+ }
333
+ const barrier = readAutonomousBarrier(lanePaths, options.lane, wave);
334
+ if (barrier) {
335
+ throw new Error(barrier.message);
336
+ }
337
+ let success = false;
338
+ for (let attempt = 1; attempt <= options.maxAttemptsPerWave; attempt += 1) {
339
+ console.log(
340
+ `\n[autonomous] launching wave ${wave} (attempt ${attempt}/${options.maxAttemptsPerWave})`,
341
+ );
342
+ const status = launchSingleWave({
343
+ ...options,
344
+ wave,
345
+ attempt,
346
+ });
347
+ reconcile(options.lane);
348
+ if (status === 0) {
349
+ launchedCount += 1;
350
+ success = true;
351
+ console.log(`[autonomous] wave ${wave} completed.`);
352
+ break;
353
+ }
354
+ console.warn(`[autonomous] wave ${wave} failed with status=${status}.`);
355
+ if (attempt < options.maxAttemptsPerWave) {
356
+ console.warn(`[autonomous] retrying wave ${wave}.`);
357
+ }
358
+ }
359
+ if (!success) {
360
+ throw new Error(`Stopping after repeated failures on wave ${wave}.`);
361
+ }
362
+ }
363
+ const finalCompleted = readRunState(lanePaths.defaultRunStatePath).completedWaves;
364
+ console.log(
365
+ `[autonomous] launched waves=${launchedCount}; completed=${finalCompleted.join(", ") || "none"}`,
366
+ );
367
+ }