@chllming/wave-orchestration 0.6.1 → 0.6.3
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 +16 -0
- package/README.md +79 -30
- package/docs/README.md +15 -3
- package/docs/concepts/context7-vs-skills.md +24 -0
- package/docs/concepts/runtime-agnostic-orchestration.md +17 -2
- package/docs/concepts/what-is-a-wave.md +28 -0
- package/docs/evals/README.md +2 -0
- package/docs/guides/terminal-surfaces.md +2 -0
- package/docs/plans/current-state.md +2 -1
- package/docs/plans/wave-orchestrator.md +22 -3
- package/docs/reference/runtime-config/README.md +4 -4
- package/docs/reference/runtime-config/claude.md +6 -1
- package/docs/reference/runtime-config/codex.md +2 -2
- package/docs/reference/runtime-config/opencode.md +1 -1
- package/docs/research/agent-context-sources.md +2 -0
- package/docs/research/coordination-failure-review.md +37 -13
- package/package.json +1 -1
- package/releases/manifest.json +33 -0
- package/scripts/wave-autonomous.mjs +2 -4
- package/scripts/wave-orchestrator/adhoc.mjs +32 -11
- package/scripts/wave-orchestrator/agent-state.mjs +10 -3
- package/scripts/wave-orchestrator/autonomous.mjs +20 -6
- package/scripts/wave-orchestrator/config.mjs +19 -0
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +150 -20
- package/scripts/wave-orchestrator/dashboard-state.mjs +8 -0
- package/scripts/wave-orchestrator/executors.mjs +67 -4
- package/scripts/wave-orchestrator/install.mjs +198 -25
- package/scripts/wave-orchestrator/launcher-runtime.mjs +1 -0
- package/scripts/wave-orchestrator/launcher.mjs +249 -10
- package/scripts/wave-orchestrator/package-update-notice.mjs +230 -0
- package/scripts/wave-orchestrator/package-version.mjs +32 -0
- package/scripts/wave-orchestrator/terminals.mjs +25 -0
- package/scripts/wave-orchestrator/wave-files.mjs +31 -0
- package/scripts/wave.mjs +12 -2
|
@@ -217,6 +217,7 @@ function buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
|
|
|
217
217
|
appendSingleValueFlag(tokens, "--agent", executor.claude.agent);
|
|
218
218
|
appendSingleValueFlag(tokens, "--permission-mode", executor.claude.permissionMode);
|
|
219
219
|
appendSingleValueFlag(tokens, "--permission-prompt-tool", executor.claude.permissionPromptTool);
|
|
220
|
+
appendSingleValueFlag(tokens, "--effort", executor.claude.effort);
|
|
220
221
|
appendSingleValueFlag(tokens, "--max-turns", executor.claude.maxTurns);
|
|
221
222
|
appendRepeatedFlag(tokens, "--mcp-config", executor.claude.mcpConfig);
|
|
222
223
|
appendSingleValueFlag(tokens, "--settings", settingsPath);
|
|
@@ -299,6 +300,55 @@ function buildLocalLaunchSpec({ promptPath, logPath }) {
|
|
|
299
300
|
};
|
|
300
301
|
}
|
|
301
302
|
|
|
303
|
+
function buildLaunchLimitsMetadata(agent) {
|
|
304
|
+
const executor = agent?.executorResolved || {};
|
|
305
|
+
const executorId = normalizeExecutorMode(executor.id || DEFAULT_EXECUTOR_MODE);
|
|
306
|
+
const attemptTimeoutMinutes = executor?.budget?.minutes ?? null;
|
|
307
|
+
if (executorId === "claude") {
|
|
308
|
+
const source = executor?.claude?.maxTurnsSource || null;
|
|
309
|
+
return {
|
|
310
|
+
attemptTimeoutMinutes,
|
|
311
|
+
knownTurnLimit: executor?.claude?.maxTurns ?? null,
|
|
312
|
+
turnLimitSource: source,
|
|
313
|
+
notes:
|
|
314
|
+
source === "budget.turns"
|
|
315
|
+
? ["Known turn limit was derived from generic budget.turns."]
|
|
316
|
+
: [],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (executorId === "opencode") {
|
|
320
|
+
const source = executor?.opencode?.stepsSource || null;
|
|
321
|
+
return {
|
|
322
|
+
attemptTimeoutMinutes,
|
|
323
|
+
knownTurnLimit: executor?.opencode?.steps ?? null,
|
|
324
|
+
turnLimitSource: source,
|
|
325
|
+
notes:
|
|
326
|
+
source === "budget.turns"
|
|
327
|
+
? ["Known turn limit was derived from generic budget.turns."]
|
|
328
|
+
: [],
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (executorId === "codex") {
|
|
332
|
+
const profileNote = executor?.codex?.profileName
|
|
333
|
+
? ` via Codex profile ${executor.codex.profileName}`
|
|
334
|
+
: "";
|
|
335
|
+
return {
|
|
336
|
+
attemptTimeoutMinutes,
|
|
337
|
+
knownTurnLimit: null,
|
|
338
|
+
turnLimitSource: "not-set-by-wave",
|
|
339
|
+
notes: [
|
|
340
|
+
`Wave emits no Codex turn-limit flag; any effective ceiling may come from the selected Codex profile${profileNote} or the upstream Codex runtime.`,
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
attemptTimeoutMinutes,
|
|
346
|
+
knownTurnLimit: null,
|
|
347
|
+
turnLimitSource: "not-applicable",
|
|
348
|
+
notes: ["Local executor does not use model turn limits."],
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
302
352
|
function buildCodexLaunchSpec({ agent, promptPath, logPath, skillProjection }) {
|
|
303
353
|
const executor = agent.executorResolved;
|
|
304
354
|
return {
|
|
@@ -329,16 +379,29 @@ function buildCodexLaunchSpec({ agent, promptPath, logPath, skillProjection }) {
|
|
|
329
379
|
export function buildExecutorLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection }) {
|
|
330
380
|
const executorId = normalizeExecutorMode(agent?.executorResolved?.id || DEFAULT_EXECUTOR_MODE);
|
|
331
381
|
ensureDirectory(overlayDir);
|
|
382
|
+
const limits = buildLaunchLimitsMetadata(agent);
|
|
332
383
|
if (executorId === "local") {
|
|
333
|
-
return
|
|
384
|
+
return {
|
|
385
|
+
...buildLocalLaunchSpec({ promptPath, logPath }),
|
|
386
|
+
limits,
|
|
387
|
+
};
|
|
334
388
|
}
|
|
335
389
|
if (executorId === "claude") {
|
|
336
|
-
return
|
|
390
|
+
return {
|
|
391
|
+
...buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection }),
|
|
392
|
+
limits,
|
|
393
|
+
};
|
|
337
394
|
}
|
|
338
395
|
if (executorId === "opencode") {
|
|
339
|
-
return
|
|
396
|
+
return {
|
|
397
|
+
...buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection }),
|
|
398
|
+
limits,
|
|
399
|
+
};
|
|
340
400
|
}
|
|
341
|
-
return
|
|
401
|
+
return {
|
|
402
|
+
...buildCodexLaunchSpec({ agent, promptPath, logPath, skillProjection }),
|
|
403
|
+
limits,
|
|
404
|
+
};
|
|
342
405
|
}
|
|
343
406
|
|
|
344
407
|
export function commandForExecutor(executor, executorId = executor?.id) {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
3
4
|
import {
|
|
4
5
|
applyContext7SelectionsToWave,
|
|
5
6
|
loadContext7BundleIndex,
|
|
6
7
|
} from "./context7.mjs";
|
|
7
8
|
import { buildLanePaths, ensureDirectory, PACKAGE_ROOT, readJsonOrNull, REPO_ROOT, writeJsonAtomic } from "./shared.mjs";
|
|
9
|
+
import { fetchLatestPackageVersion } from "./package-update-notice.mjs";
|
|
10
|
+
import {
|
|
11
|
+
compareVersions,
|
|
12
|
+
readInstalledPackageMetadata,
|
|
13
|
+
WAVE_PACKAGE_NAME,
|
|
14
|
+
} from "./package-version.mjs";
|
|
8
15
|
import { loadWaveConfig } from "./config.mjs";
|
|
9
16
|
import { applyExecutorSelectionsToWave, parseWaveFiles, validateWaveDefinition } from "./wave-files.mjs";
|
|
10
17
|
import { validateLaneSkillConfiguration } from "./skills.mjs";
|
|
@@ -14,7 +21,7 @@ export const INSTALL_STATE_DIR = ".wave";
|
|
|
14
21
|
export const INSTALL_STATE_PATH = path.join(REPO_ROOT, INSTALL_STATE_DIR, "install-state.json");
|
|
15
22
|
export const UPGRADE_HISTORY_DIR = path.join(REPO_ROOT, INSTALL_STATE_DIR, "upgrade-history");
|
|
16
23
|
export const CHANGELOG_MANIFEST_PATH = path.join(PACKAGE_ROOT, "releases", "manifest.json");
|
|
17
|
-
export const
|
|
24
|
+
export const WORKSPACE_PACKAGE_JSON_PATH = path.join(REPO_ROOT, "package.json");
|
|
18
25
|
export const STARTER_TEMPLATE_PATHS = [
|
|
19
26
|
"wave.config.json",
|
|
20
27
|
"docs/README.md",
|
|
@@ -69,11 +76,7 @@ function collectDeclaredDeployKinds(waves = []) {
|
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
function packageMetadata() {
|
|
72
|
-
|
|
73
|
-
if (!payload?.name || !payload?.version) {
|
|
74
|
-
throw new Error(`Invalid package metadata: ${PACKAGE_METADATA_PATH}`);
|
|
75
|
-
}
|
|
76
|
-
return payload;
|
|
79
|
+
return readInstalledPackageMetadata();
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
function readInstallState() {
|
|
@@ -149,25 +152,6 @@ function nextHistoryRecord(existingState, entry) {
|
|
|
149
152
|
return history;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
function normalizeVersionParts(version) {
|
|
153
|
-
return String(version || "")
|
|
154
|
-
.split(".")
|
|
155
|
-
.map((part) => Number.parseInt(part.replace(/[^0-9].*$/, ""), 10) || 0);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function compareVersions(a, b) {
|
|
159
|
-
const left = normalizeVersionParts(a);
|
|
160
|
-
const right = normalizeVersionParts(b);
|
|
161
|
-
const length = Math.max(left.length, right.length);
|
|
162
|
-
for (let index = 0; index < length; index += 1) {
|
|
163
|
-
const diff = (left[index] || 0) - (right[index] || 0);
|
|
164
|
-
if (diff !== 0) {
|
|
165
|
-
return diff;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return 0;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
155
|
function readChangelogManifest() {
|
|
172
156
|
const payload = readJsonOrNull(CHANGELOG_MANIFEST_PATH);
|
|
173
157
|
if (!payload?.releases || !Array.isArray(payload.releases)) {
|
|
@@ -478,6 +462,186 @@ export function upgradeWorkspace() {
|
|
|
478
462
|
};
|
|
479
463
|
}
|
|
480
464
|
|
|
465
|
+
function readWorkspacePackageManifest(workspaceRoot = REPO_ROOT) {
|
|
466
|
+
const payload = readJsonOrNull(path.join(workspaceRoot, "package.json"));
|
|
467
|
+
if (!payload || typeof payload !== "object") {
|
|
468
|
+
throw new Error(`Missing package.json at ${path.join(workspaceRoot, "package.json")}`);
|
|
469
|
+
}
|
|
470
|
+
return payload;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function readInstallStateForWorkspace(workspaceRoot = REPO_ROOT) {
|
|
474
|
+
const payload = readJsonOrNull(path.join(workspaceRoot, INSTALL_STATE_DIR, "install-state.json"));
|
|
475
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function parsePackageManagerId(value) {
|
|
479
|
+
const normalized = String(value || "")
|
|
480
|
+
.trim()
|
|
481
|
+
.toLowerCase();
|
|
482
|
+
if (!normalized) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
if (normalized.startsWith("pnpm@")) {
|
|
486
|
+
return "pnpm";
|
|
487
|
+
}
|
|
488
|
+
if (normalized.startsWith("npm@")) {
|
|
489
|
+
return "npm";
|
|
490
|
+
}
|
|
491
|
+
if (normalized.startsWith("yarn@")) {
|
|
492
|
+
return "yarn";
|
|
493
|
+
}
|
|
494
|
+
if (normalized.startsWith("bun@")) {
|
|
495
|
+
return "bun";
|
|
496
|
+
}
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function detectWorkspacePackageManager(workspaceRoot = REPO_ROOT) {
|
|
501
|
+
const manifest = readWorkspacePackageManifest(workspaceRoot);
|
|
502
|
+
const packageManagerFromManifest = parsePackageManagerId(manifest.packageManager);
|
|
503
|
+
if (packageManagerFromManifest) {
|
|
504
|
+
return {
|
|
505
|
+
id: packageManagerFromManifest,
|
|
506
|
+
source: "packageManager",
|
|
507
|
+
raw: manifest.packageManager,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
for (const [fileName, id] of [
|
|
511
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
512
|
+
["package-lock.json", "npm"],
|
|
513
|
+
["npm-shrinkwrap.json", "npm"],
|
|
514
|
+
["yarn.lock", "yarn"],
|
|
515
|
+
["bun.lockb", "bun"],
|
|
516
|
+
["bun.lock", "bun"],
|
|
517
|
+
]) {
|
|
518
|
+
if (fs.existsSync(path.join(workspaceRoot, fileName))) {
|
|
519
|
+
return {
|
|
520
|
+
id,
|
|
521
|
+
source: "lockfile",
|
|
522
|
+
raw: fileName,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
id: "npm",
|
|
528
|
+
source: "default",
|
|
529
|
+
raw: null,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function packageManagerCommands(managerId, packageName = WAVE_PACKAGE_NAME) {
|
|
534
|
+
if (managerId === "pnpm") {
|
|
535
|
+
return {
|
|
536
|
+
install: ["pnpm", ["add", "-D", `${packageName}@latest`]],
|
|
537
|
+
execWave: (args) => ["pnpm", ["exec", "wave", ...args]],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
if (managerId === "npm") {
|
|
541
|
+
return {
|
|
542
|
+
install: ["npm", ["install", "--save-dev", `${packageName}@latest`]],
|
|
543
|
+
execWave: (args) => ["npm", ["exec", "--", "wave", ...args]],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (managerId === "yarn") {
|
|
547
|
+
return {
|
|
548
|
+
install: ["yarn", ["add", "-D", `${packageName}@latest`]],
|
|
549
|
+
execWave: (args) => ["yarn", ["exec", "wave", ...args]],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (managerId === "bun") {
|
|
553
|
+
return {
|
|
554
|
+
install: ["bun", ["add", "-d", `${packageName}@latest`]],
|
|
555
|
+
execWave: (args) => ["bun", ["x", "wave", ...args]],
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
throw new Error(`Unsupported package manager: ${managerId}`);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function runCommandOrThrow(command, args, options = {}) {
|
|
562
|
+
const spawnImpl = options.spawnImpl || spawnSync;
|
|
563
|
+
const result = spawnImpl(command, args, {
|
|
564
|
+
cwd: options.workspaceRoot || REPO_ROOT,
|
|
565
|
+
stdio: options.stdio || "inherit",
|
|
566
|
+
env: options.env || process.env,
|
|
567
|
+
encoding: "utf8",
|
|
568
|
+
});
|
|
569
|
+
const status = Number.isInteger(result?.status) ? result.status : 1;
|
|
570
|
+
if (status !== 0) {
|
|
571
|
+
throw new Error(`${command} ${args.join(" ")} failed with status ${status}`);
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export async function selfUpdateWorkspace(options = {}) {
|
|
577
|
+
const workspaceRoot = options.workspaceRoot || REPO_ROOT;
|
|
578
|
+
const metadata = options.packageMetadata || packageMetadata();
|
|
579
|
+
const installState = readInstallStateForWorkspace(workspaceRoot);
|
|
580
|
+
const packageManager = detectWorkspacePackageManager(workspaceRoot);
|
|
581
|
+
const commands = packageManagerCommands(packageManager.id, metadata.name || WAVE_PACKAGE_NAME);
|
|
582
|
+
const emit = options.emit || console.log;
|
|
583
|
+
let latestVersion = null;
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
latestVersion = await fetchLatestPackageVersion(metadata.name || WAVE_PACKAGE_NAME, {
|
|
587
|
+
fetchImpl: options.fetchImpl,
|
|
588
|
+
timeoutMs: options.timeoutMs,
|
|
589
|
+
});
|
|
590
|
+
} catch {
|
|
591
|
+
latestVersion = null;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const currentVersion = String(metadata.version || "").trim();
|
|
595
|
+
const recordedVersion = String(installState?.installedVersion || "").trim() || null;
|
|
596
|
+
const needsUpgradeOnly = recordedVersion && compareVersions(currentVersion, recordedVersion) !== 0;
|
|
597
|
+
|
|
598
|
+
emit(`[wave:self-update] package_manager=${packageManager.id}`);
|
|
599
|
+
|
|
600
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) <= 0) {
|
|
601
|
+
if (!needsUpgradeOnly) {
|
|
602
|
+
emit(`[wave:self-update] ${metadata.name} is already current at ${currentVersion}.`);
|
|
603
|
+
return {
|
|
604
|
+
mode: "already-current",
|
|
605
|
+
packageManager: packageManager.id,
|
|
606
|
+
currentVersion,
|
|
607
|
+
latestVersion,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
emit(
|
|
611
|
+
`[wave:self-update] dependency is already at ${currentVersion}; recording workspace upgrade state.`,
|
|
612
|
+
);
|
|
613
|
+
const [upgradeCommand, upgradeArgs] = commands.execWave(["upgrade"]);
|
|
614
|
+
runCommandOrThrow(upgradeCommand, upgradeArgs, options);
|
|
615
|
+
return {
|
|
616
|
+
mode: "upgrade-only",
|
|
617
|
+
packageManager: packageManager.id,
|
|
618
|
+
currentVersion,
|
|
619
|
+
latestVersion,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
emit(
|
|
624
|
+
`[wave:self-update] updating ${metadata.name} from ${currentVersion}${latestVersion ? ` to ${latestVersion}` : " to the latest published version"}.`,
|
|
625
|
+
);
|
|
626
|
+
const [installCommand, installArgs] = commands.install;
|
|
627
|
+
runCommandOrThrow(installCommand, installArgs, options);
|
|
628
|
+
|
|
629
|
+
emit("[wave:self-update] release notes since the recorded install:");
|
|
630
|
+
const [changelogCommand, changelogArgs] = commands.execWave(["changelog", "--since-installed"]);
|
|
631
|
+
runCommandOrThrow(changelogCommand, changelogArgs, options);
|
|
632
|
+
|
|
633
|
+
emit("[wave:self-update] recording install-state and upgrade report:");
|
|
634
|
+
const [upgradeCommand, upgradeArgs] = commands.execWave(["upgrade"]);
|
|
635
|
+
runCommandOrThrow(upgradeCommand, upgradeArgs, options);
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
mode: "updated",
|
|
639
|
+
packageManager: packageManager.id,
|
|
640
|
+
currentVersion,
|
|
641
|
+
latestVersion,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
481
645
|
function printJson(payload) {
|
|
482
646
|
console.log(JSON.stringify(payload, null, 2));
|
|
483
647
|
}
|
|
@@ -486,6 +650,7 @@ function printHelp() {
|
|
|
486
650
|
console.log(`Usage:
|
|
487
651
|
wave init [--adopt-existing] [--json]
|
|
488
652
|
wave upgrade [--json]
|
|
653
|
+
wave self-update
|
|
489
654
|
wave changelog [--since-installed] [--json]
|
|
490
655
|
wave doctor [--json]
|
|
491
656
|
`);
|
|
@@ -562,6 +727,14 @@ export async function runInstallCli(argv) {
|
|
|
562
727
|
return;
|
|
563
728
|
}
|
|
564
729
|
|
|
730
|
+
if (subcommand === "self-update") {
|
|
731
|
+
if (options.json) {
|
|
732
|
+
throw new Error("`wave self-update` does not support --json.");
|
|
733
|
+
}
|
|
734
|
+
await selfUpdateWorkspace();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
565
738
|
if (subcommand === "changelog") {
|
|
566
739
|
const result = readChangelog({ sinceInstalled: options.sinceInstalled });
|
|
567
740
|
if (options.json) {
|
|
@@ -139,6 +139,7 @@ export async function launchAgentSession(lanePaths, params, { runTmuxFn }) {
|
|
|
139
139
|
env: launchSpec.env || {},
|
|
140
140
|
useRateLimitRetries: launchSpec.useRateLimitRetries === true,
|
|
141
141
|
invocationLines: launchSpec.invocationLines,
|
|
142
|
+
limits: launchSpec.limits || null,
|
|
142
143
|
skills: summarizeResolvedSkills(agent.skillsResolved),
|
|
143
144
|
});
|
|
144
145
|
return {
|