@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.
- package/CHANGELOG.md +41 -0
- package/README.md +549 -0
- package/docs/agents/wave-deploy-verifier-role.md +34 -0
- package/docs/agents/wave-documentation-role.md +30 -0
- package/docs/agents/wave-evaluator-role.md +43 -0
- package/docs/agents/wave-infra-role.md +34 -0
- package/docs/agents/wave-integration-role.md +32 -0
- package/docs/agents/wave-launcher-role.md +37 -0
- package/docs/context7/bundles.json +91 -0
- package/docs/plans/component-cutover-matrix.json +112 -0
- package/docs/plans/component-cutover-matrix.md +49 -0
- package/docs/plans/context7-wave-orchestrator.md +130 -0
- package/docs/plans/current-state.md +44 -0
- package/docs/plans/master-plan.md +16 -0
- package/docs/plans/migration.md +23 -0
- package/docs/plans/wave-orchestrator.md +254 -0
- package/docs/plans/waves/wave-0.md +165 -0
- package/docs/reference/github-packages-setup.md +52 -0
- package/docs/reference/migration-0.2-to-0.5.md +622 -0
- package/docs/reference/npmjs-trusted-publishing.md +55 -0
- package/docs/reference/repository-guidance.md +18 -0
- package/docs/reference/runtime-config/README.md +85 -0
- package/docs/reference/runtime-config/claude.md +105 -0
- package/docs/reference/runtime-config/codex.md +81 -0
- package/docs/reference/runtime-config/opencode.md +93 -0
- package/docs/research/agent-context-sources.md +57 -0
- package/docs/roadmap.md +626 -0
- package/package.json +53 -0
- package/releases/manifest.json +101 -0
- package/scripts/context7-api-check.sh +21 -0
- package/scripts/context7-export-env.sh +52 -0
- package/scripts/research/agent-context-archive.mjs +472 -0
- package/scripts/research/generate-agent-context-indexes.mjs +85 -0
- package/scripts/research/import-agent-context-archive.mjs +793 -0
- package/scripts/research/manifests/harness-and-blackboard-2026-03-21.mjs +201 -0
- package/scripts/wave-autonomous.mjs +13 -0
- package/scripts/wave-cli-bootstrap.mjs +27 -0
- package/scripts/wave-dashboard.mjs +11 -0
- package/scripts/wave-human-feedback.mjs +11 -0
- package/scripts/wave-launcher.mjs +11 -0
- package/scripts/wave-local-executor.mjs +13 -0
- package/scripts/wave-orchestrator/agent-state.mjs +416 -0
- package/scripts/wave-orchestrator/autonomous.mjs +367 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +605 -0
- package/scripts/wave-orchestrator/config.mjs +848 -0
- package/scripts/wave-orchestrator/context7.mjs +464 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +286 -0
- package/scripts/wave-orchestrator/coordination-store.mjs +987 -0
- package/scripts/wave-orchestrator/coordination.mjs +768 -0
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +254 -0
- package/scripts/wave-orchestrator/dashboard-state.mjs +473 -0
- package/scripts/wave-orchestrator/dep-cli.mjs +219 -0
- package/scripts/wave-orchestrator/docs-queue.mjs +75 -0
- package/scripts/wave-orchestrator/executors.mjs +385 -0
- package/scripts/wave-orchestrator/feedback.mjs +372 -0
- package/scripts/wave-orchestrator/install.mjs +540 -0
- package/scripts/wave-orchestrator/launcher.mjs +3879 -0
- package/scripts/wave-orchestrator/ledger.mjs +332 -0
- package/scripts/wave-orchestrator/local-executor.mjs +263 -0
- package/scripts/wave-orchestrator/replay.mjs +246 -0
- package/scripts/wave-orchestrator/roots.mjs +10 -0
- package/scripts/wave-orchestrator/routing-state.mjs +542 -0
- package/scripts/wave-orchestrator/shared.mjs +405 -0
- package/scripts/wave-orchestrator/terminals.mjs +209 -0
- package/scripts/wave-orchestrator/traces.mjs +1094 -0
- package/scripts/wave-orchestrator/wave-files.mjs +1923 -0
- package/scripts/wave.mjs +103 -0
- package/wave.config.json +115 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { analyzeMessageBoardCommunication } from "./coordination.mjs";
|
|
4
|
+
import { commsAgeSummary, deploymentSummary, renderCountsByState } from "./dashboard-state.mjs";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_REFRESH_MS,
|
|
7
|
+
DEFAULT_WAVE_LANE,
|
|
8
|
+
FINAL_EXIT_DELAY_MS,
|
|
9
|
+
REPO_ROOT,
|
|
10
|
+
TERMINAL_STATES,
|
|
11
|
+
formatAgeFromTimestamp,
|
|
12
|
+
formatElapsed,
|
|
13
|
+
pad,
|
|
14
|
+
sleep,
|
|
15
|
+
truncate,
|
|
16
|
+
} from "./shared.mjs";
|
|
17
|
+
|
|
18
|
+
export function parseDashboardArgs(argv) {
|
|
19
|
+
const options = {
|
|
20
|
+
lane: DEFAULT_WAVE_LANE,
|
|
21
|
+
dashboardFile: null,
|
|
22
|
+
messageBoard: null,
|
|
23
|
+
watch: false,
|
|
24
|
+
refreshMs: DEFAULT_REFRESH_MS,
|
|
25
|
+
};
|
|
26
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
27
|
+
const arg = argv[i];
|
|
28
|
+
if (arg === "--") {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (arg === "--watch") {
|
|
32
|
+
options.watch = true;
|
|
33
|
+
} else if (arg === "--lane") {
|
|
34
|
+
options.lane =
|
|
35
|
+
String(argv[++i] || "")
|
|
36
|
+
.trim()
|
|
37
|
+
.toLowerCase() || DEFAULT_WAVE_LANE;
|
|
38
|
+
} else if (arg === "--dashboard-file") {
|
|
39
|
+
options.dashboardFile = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
40
|
+
} else if (arg === "--message-board") {
|
|
41
|
+
options.messageBoard = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
42
|
+
} else if (arg === "--refresh-ms") {
|
|
43
|
+
options.refreshMs = Number.parseInt(String(argv[++i] || ""), 10);
|
|
44
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
45
|
+
return { help: true, options };
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!options.dashboardFile) {
|
|
51
|
+
throw new Error("--dashboard-file is required");
|
|
52
|
+
}
|
|
53
|
+
return { help: false, options };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readMessageBoardTail(messageBoardPath, maxLines = 24) {
|
|
57
|
+
if (!messageBoardPath) {
|
|
58
|
+
return ["(message board path unavailable)"];
|
|
59
|
+
}
|
|
60
|
+
if (!fs.existsSync(messageBoardPath)) {
|
|
61
|
+
return ["(message board missing)"];
|
|
62
|
+
}
|
|
63
|
+
const raw = fs.readFileSync(messageBoardPath, "utf8").trim();
|
|
64
|
+
if (!raw) {
|
|
65
|
+
return ["(message board currently empty)"];
|
|
66
|
+
}
|
|
67
|
+
return raw.split(/\r?\n/).slice(-maxLines);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveMessageBoardPath(state, overridePath) {
|
|
71
|
+
if (overridePath) {
|
|
72
|
+
return overridePath;
|
|
73
|
+
}
|
|
74
|
+
if (typeof state?.messageBoardPath === "string") {
|
|
75
|
+
return path.resolve(REPO_ROOT, state.messageBoardPath);
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isGlobalDashboardState(state) {
|
|
81
|
+
return Boolean(state && Array.isArray(state.waves) && !Array.isArray(state.agents));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function renderWaveDashboard({ state, dashboardPath, messageBoardPath, lane }) {
|
|
85
|
+
if (!state) {
|
|
86
|
+
return `Dashboard file not found or invalid: ${dashboardPath}`;
|
|
87
|
+
}
|
|
88
|
+
const lines = [];
|
|
89
|
+
const laneName = String(state?.lane || lane || "").trim() || DEFAULT_WAVE_LANE;
|
|
90
|
+
lines.push(
|
|
91
|
+
`${laneName} Wave Dashboard | wave=${state.wave ?? "?"} | status=${state.status ?? "unknown"} | attempt=${state.attempt ?? "?"}/${state.maxAttempts ?? "?"}`,
|
|
92
|
+
);
|
|
93
|
+
lines.push(
|
|
94
|
+
`Updated ${formatAgeFromTimestamp(Date.parse(state.updatedAt || ""))} | Started ${state.startedAt || "n/a"} | Elapsed ${formatElapsed(state.startedAt)}`,
|
|
95
|
+
);
|
|
96
|
+
lines.push(`Run tag: ${state.runTag || "n/a"} | Wave file: ${state.waveFile || "n/a"}`);
|
|
97
|
+
lines.push(`Counts: ${renderCountsByState(state.agents || []) || "none"}`);
|
|
98
|
+
const comms = analyzeMessageBoardCommunication(messageBoardPath);
|
|
99
|
+
if (!comms.available) {
|
|
100
|
+
lines.push(`Comms: unavailable ${comms.reason || ""}`.trim());
|
|
101
|
+
} else {
|
|
102
|
+
lines.push(
|
|
103
|
+
`Comms: requests=${comms.actionableRequests} unresolved=${comms.unresolvedRequests} unacknowledged=${comms.unacknowledgedRequests} malformed=${comms.malformedEntries} placeholder-ts=${comms.placeholderTimestampEntries}`,
|
|
104
|
+
);
|
|
105
|
+
lines.push(
|
|
106
|
+
`Comms age: last-ack=${commsAgeSummary(comms.lastAcknowledgementTimestamp)} oldest-unack=${commsAgeSummary(comms.oldestUnacknowledgedTimestamp)}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
lines.push("");
|
|
110
|
+
lines.push("Agents:");
|
|
111
|
+
lines.push(
|
|
112
|
+
`${pad("ID", 8)} ${pad("State", 12)} ${pad("Attempts", 8)} ${pad("Exit", 6)} ${pad("Deploy", 24)} ${pad("Last Update", 12)} Detail`,
|
|
113
|
+
);
|
|
114
|
+
lines.push(
|
|
115
|
+
`${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(24)} ${"-".repeat(12)} ${"-".repeat(36)}`,
|
|
116
|
+
);
|
|
117
|
+
for (const agent of state.agents || []) {
|
|
118
|
+
lines.push(
|
|
119
|
+
`${pad(agent.agentId || "-", 8)} ${pad(agent.state || "-", 12)} ${pad(agent.attempts ?? 0, 8)} ${pad(
|
|
120
|
+
agent.exitCode ?? "-",
|
|
121
|
+
6,
|
|
122
|
+
)} ${pad(deploymentSummary({ service: agent.deploymentService, state: agent.deploymentState }), 24)} ${pad(
|
|
123
|
+
formatAgeFromTimestamp(Date.parse(agent.lastUpdateAt || "")),
|
|
124
|
+
12,
|
|
125
|
+
)} ${truncate(agent.detail || "", 72)}`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push("Recent events:");
|
|
130
|
+
for (const event of (state.events || []).slice(-12)) {
|
|
131
|
+
const prefix = event.agentId ? `[${event.agentId}]` : "[wave]";
|
|
132
|
+
lines.push(
|
|
133
|
+
`${event.at || "n/a"} ${pad(event.level || "info", 5)} ${prefix} ${event.message || ""}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if ((state.events || []).length === 0) {
|
|
137
|
+
lines.push("(none)");
|
|
138
|
+
}
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push("Rolling message board:");
|
|
141
|
+
lines.push(`Path: ${messageBoardPath || "n/a"}`);
|
|
142
|
+
lines.push(...readMessageBoardTail(messageBoardPath));
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderGlobalDashboard({ state, dashboardPath, lane }) {
|
|
147
|
+
if (!state) {
|
|
148
|
+
return `Dashboard file not found or invalid: ${dashboardPath}`;
|
|
149
|
+
}
|
|
150
|
+
const lines = [];
|
|
151
|
+
const laneName = String(state?.lane || lane || "").trim() || DEFAULT_WAVE_LANE;
|
|
152
|
+
lines.push(
|
|
153
|
+
`${laneName} Wave Global Dashboard | run=${state.runId || "n/a"} | status=${state.status || "unknown"}`,
|
|
154
|
+
);
|
|
155
|
+
lines.push(
|
|
156
|
+
`Updated ${formatAgeFromTimestamp(Date.parse(state.updatedAt || ""))} | Started ${state.startedAt || "n/a"} | Elapsed ${formatElapsed(state.startedAt)}`,
|
|
157
|
+
);
|
|
158
|
+
lines.push(
|
|
159
|
+
`Options: autoNext=${state.options?.autoNext ? "true" : "false"} start=${state.options?.startWave ?? "?"} end=${state.options?.endWave ?? "last"} retries=${state.options?.maxRetriesPerWave ?? "?"}`,
|
|
160
|
+
);
|
|
161
|
+
lines.push("");
|
|
162
|
+
lines.push("Waves:");
|
|
163
|
+
lines.push(
|
|
164
|
+
`${pad("Wave", 6)} ${pad("Status", 10)} ${pad("Attempt", 9)} ${pad("Agents", 16)} ${pad("Started", 12)} ${pad("Last Message", 70)}`,
|
|
165
|
+
);
|
|
166
|
+
lines.push(
|
|
167
|
+
`${"-".repeat(6)} ${"-".repeat(10)} ${"-".repeat(9)} ${"-".repeat(16)} ${"-".repeat(12)} ${"-".repeat(70)}`,
|
|
168
|
+
);
|
|
169
|
+
for (const wave of state.waves || []) {
|
|
170
|
+
const agents = `${wave.agentsCompleted ?? 0}/${wave.agentsTotal ?? 0} ok, ${wave.agentsFailed ?? 0} fail`;
|
|
171
|
+
lines.push(
|
|
172
|
+
`${pad(wave.wave ?? "-", 6)} ${pad(wave.status || "-", 10)} ${pad(
|
|
173
|
+
`${wave.attempt ?? 0}/${wave.maxAttempts ?? 0}`,
|
|
174
|
+
9,
|
|
175
|
+
)} ${pad(agents, 16)} ${pad(
|
|
176
|
+
formatAgeFromTimestamp(Date.parse(wave.startedAt || "")),
|
|
177
|
+
12,
|
|
178
|
+
)} ${truncate(wave.lastMessage || "", 70)}`,
|
|
179
|
+
);
|
|
180
|
+
const deployments = Array.isArray(wave.deployments) ? wave.deployments : [];
|
|
181
|
+
if (deployments.length > 0) {
|
|
182
|
+
const deployLine = deployments
|
|
183
|
+
.slice(-3)
|
|
184
|
+
.map((deployment) => `${deployment.agentId}:${deployment.service}:${deployment.state}`)
|
|
185
|
+
.join(" | ");
|
|
186
|
+
lines.push(` Deploy: ${truncate(deployLine, 120)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push("Recent events:");
|
|
191
|
+
for (const event of (state.events || []).slice(-16)) {
|
|
192
|
+
const waveTag = event.wave ? `wave:${event.wave}` : "wave:-";
|
|
193
|
+
lines.push(
|
|
194
|
+
`${event.at || "n/a"} ${pad(event.level || "info", 5)} [${waveTag}] ${event.message || ""}`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
if ((state.events || []).length === 0) {
|
|
198
|
+
lines.push("(none)");
|
|
199
|
+
}
|
|
200
|
+
return lines.join("\n");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function renderDashboard({ state, dashboardPath, messageBoardPath, lane }) {
|
|
204
|
+
return isGlobalDashboardState(state)
|
|
205
|
+
? renderGlobalDashboard({ state, dashboardPath, lane })
|
|
206
|
+
: renderWaveDashboard({ state, dashboardPath, messageBoardPath, lane });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function runDashboardCli(argv) {
|
|
210
|
+
const { help, options } = parseDashboardArgs(argv);
|
|
211
|
+
if (help) {
|
|
212
|
+
console.log(`Usage: pnpm exec wave dashboard --dashboard-file <path> [options]
|
|
213
|
+
|
|
214
|
+
Options:
|
|
215
|
+
--lane <name> Wave lane name (default: ${DEFAULT_WAVE_LANE})
|
|
216
|
+
--dashboard-file <path> Path to wave/global dashboard JSON
|
|
217
|
+
--message-board <path> Optional message board path override
|
|
218
|
+
--watch Refresh continuously
|
|
219
|
+
--refresh-ms <n> Refresh interval in ms (default: ${DEFAULT_REFRESH_MS})
|
|
220
|
+
`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let terminalStateReachedAt = null;
|
|
225
|
+
while (true) {
|
|
226
|
+
const raw = fs.existsSync(options.dashboardFile)
|
|
227
|
+
? JSON.parse(fs.readFileSync(options.dashboardFile, "utf8"))
|
|
228
|
+
: null;
|
|
229
|
+
const boardPath = resolveMessageBoardPath(raw, options.messageBoard);
|
|
230
|
+
const rendered = renderDashboard({
|
|
231
|
+
state: raw,
|
|
232
|
+
dashboardPath: options.dashboardFile,
|
|
233
|
+
messageBoardPath: boardPath,
|
|
234
|
+
lane: options.lane,
|
|
235
|
+
});
|
|
236
|
+
if (process.stdout.isTTY) {
|
|
237
|
+
process.stdout.write("\u001bc");
|
|
238
|
+
}
|
|
239
|
+
process.stdout.write(`${rendered}\n`);
|
|
240
|
+
if (!options.watch) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const currentStatus = raw?.status || "";
|
|
244
|
+
if (TERMINAL_STATES.has(currentStatus)) {
|
|
245
|
+
terminalStateReachedAt ??= Date.now();
|
|
246
|
+
if (Date.now() - terminalStateReachedAt >= FINAL_EXIT_DELAY_MS) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
terminalStateReachedAt = null;
|
|
251
|
+
}
|
|
252
|
+
await sleep(options.refreshMs);
|
|
253
|
+
}
|
|
254
|
+
}
|