@chllming/wave-orchestration 0.6.1 → 0.6.2
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 +9 -0
- package/README.md +75 -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/wave-orchestrator.md +11 -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 +18 -0
- package/scripts/wave-orchestrator/agent-state.mjs +10 -3
- 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/launcher-runtime.mjs +1 -0
- package/scripts/wave-orchestrator/launcher.mjs +245 -10
- package/scripts/wave-orchestrator/terminals.mjs +25 -0
- package/scripts/wave-orchestrator/wave-files.mjs +31 -0
|
@@ -81,6 +81,7 @@ import {
|
|
|
81
81
|
writeTextAtomic,
|
|
82
82
|
} from "./shared.mjs";
|
|
83
83
|
import {
|
|
84
|
+
createCurrentWaveDashboardTerminalEntry,
|
|
84
85
|
appendTerminalEntries,
|
|
85
86
|
createGlobalDashboardTerminalEntry,
|
|
86
87
|
createTemporaryTerminalEntries,
|
|
@@ -1449,11 +1450,94 @@ export function readWaveImplementationGate(wave, agentRuns) {
|
|
|
1449
1450
|
};
|
|
1450
1451
|
}
|
|
1451
1452
|
|
|
1453
|
+
function analyzePromotedComponentOwners(componentId, agentRuns, summariesByAgentId) {
|
|
1454
|
+
const ownerRuns = (agentRuns || []).filter((runInfo) =>
|
|
1455
|
+
runInfo.agent.components?.includes(componentId),
|
|
1456
|
+
);
|
|
1457
|
+
const ownerAgentIds = ownerRuns.map((runInfo) => runInfo.agent.agentId);
|
|
1458
|
+
const satisfiedAgentIds = [];
|
|
1459
|
+
const waitingOnAgentIds = [];
|
|
1460
|
+
const failedOwnContractAgentIds = [];
|
|
1461
|
+
for (const runInfo of ownerRuns) {
|
|
1462
|
+
const summary = summariesByAgentId?.[runInfo.agent.agentId] || null;
|
|
1463
|
+
const implementationValidation = validateImplementationSummary(runInfo.agent, summary);
|
|
1464
|
+
const componentMarkers = new Map(
|
|
1465
|
+
Array.isArray(summary?.components)
|
|
1466
|
+
? summary.components.map((component) => [component.componentId, component])
|
|
1467
|
+
: [],
|
|
1468
|
+
);
|
|
1469
|
+
const marker = componentMarkers.get(componentId);
|
|
1470
|
+
const expectedLevel = runInfo.agent.componentTargets?.[componentId] || null;
|
|
1471
|
+
const componentSatisfied =
|
|
1472
|
+
marker &&
|
|
1473
|
+
marker.state === "met" &&
|
|
1474
|
+
(!expectedLevel || marker.level === expectedLevel);
|
|
1475
|
+
if (implementationValidation.ok && componentSatisfied) {
|
|
1476
|
+
satisfiedAgentIds.push(runInfo.agent.agentId);
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
waitingOnAgentIds.push(runInfo.agent.agentId);
|
|
1480
|
+
if (!implementationValidation.ok) {
|
|
1481
|
+
failedOwnContractAgentIds.push(runInfo.agent.agentId);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return {
|
|
1485
|
+
componentId,
|
|
1486
|
+
ownerRuns,
|
|
1487
|
+
ownerAgentIds,
|
|
1488
|
+
satisfiedAgentIds,
|
|
1489
|
+
waitingOnAgentIds,
|
|
1490
|
+
failedOwnContractAgentIds,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function buildSharedComponentSiblingPendingFailure(componentState) {
|
|
1495
|
+
if (
|
|
1496
|
+
!componentState ||
|
|
1497
|
+
componentState.satisfiedAgentIds.length === 0 ||
|
|
1498
|
+
componentState.waitingOnAgentIds.length === 0
|
|
1499
|
+
) {
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
const landedSummary =
|
|
1503
|
+
componentState.satisfiedAgentIds.length === 1
|
|
1504
|
+
? `${componentState.satisfiedAgentIds[0]} desired-state slice landed`
|
|
1505
|
+
: `${componentState.satisfiedAgentIds.join(", ")} desired-state slices landed`;
|
|
1506
|
+
const ownerRun =
|
|
1507
|
+
componentState.ownerRuns.find((runInfo) =>
|
|
1508
|
+
componentState.waitingOnAgentIds.includes(runInfo.agent.agentId),
|
|
1509
|
+
) ||
|
|
1510
|
+
componentState.ownerRuns[0] ||
|
|
1511
|
+
null;
|
|
1512
|
+
return {
|
|
1513
|
+
ok: false,
|
|
1514
|
+
agentId: componentState.waitingOnAgentIds[0] || ownerRun?.agent?.agentId || null,
|
|
1515
|
+
componentId: componentState.componentId || null,
|
|
1516
|
+
statusCode: "shared-component-sibling-pending",
|
|
1517
|
+
detail: `${landedSummary}; shared component closure still depends on ${componentState.waitingOnAgentIds.join("/")}.`,
|
|
1518
|
+
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
1519
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
1520
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
1521
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
1522
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1452
1526
|
export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
1453
1527
|
const summariesByAgentId = Object.fromEntries(
|
|
1454
1528
|
agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
1455
1529
|
);
|
|
1456
1530
|
const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
1531
|
+
const sharedPending = (wave.componentPromotions || [])
|
|
1532
|
+
.map((promotion) =>
|
|
1533
|
+
buildSharedComponentSiblingPendingFailure(
|
|
1534
|
+
analyzePromotedComponentOwners(promotion.componentId, agentRuns, summariesByAgentId),
|
|
1535
|
+
),
|
|
1536
|
+
)
|
|
1537
|
+
.find(Boolean);
|
|
1538
|
+
if (sharedPending) {
|
|
1539
|
+
return sharedPending;
|
|
1540
|
+
}
|
|
1457
1541
|
if (validation.ok) {
|
|
1458
1542
|
return {
|
|
1459
1543
|
ok: true,
|
|
@@ -1464,8 +1548,12 @@ export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
|
1464
1548
|
logPath: null,
|
|
1465
1549
|
};
|
|
1466
1550
|
}
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1551
|
+
const componentState = analyzePromotedComponentOwners(
|
|
1552
|
+
validation.componentId,
|
|
1553
|
+
agentRuns,
|
|
1554
|
+
summariesByAgentId,
|
|
1555
|
+
);
|
|
1556
|
+
const ownerRun = componentState.ownerRuns[0] ?? null;
|
|
1469
1557
|
return {
|
|
1470
1558
|
ok: false,
|
|
1471
1559
|
agentId: ownerRun?.agent?.agentId || null,
|
|
@@ -1473,6 +1561,10 @@ export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
|
1473
1561
|
statusCode: validation.statusCode,
|
|
1474
1562
|
detail: validation.detail,
|
|
1475
1563
|
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
1564
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
1565
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
1566
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
1567
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
1476
1568
|
};
|
|
1477
1569
|
}
|
|
1478
1570
|
|
|
@@ -1799,6 +1891,38 @@ function removeOrphanWaveDashboards(lanePaths, activeSessionNames) {
|
|
|
1799
1891
|
return removedDashboardPaths;
|
|
1800
1892
|
}
|
|
1801
1893
|
|
|
1894
|
+
function pruneDryRunExecutorPreviewDirs(lanePaths, waves) {
|
|
1895
|
+
if (!fs.existsSync(lanePaths.executorOverlaysDir)) {
|
|
1896
|
+
return [];
|
|
1897
|
+
}
|
|
1898
|
+
const expectedSlugsByWave = new Map(
|
|
1899
|
+
(waves || []).map((wave) => [wave.wave, new Set((wave.agents || []).map((agent) => agent.slug))]),
|
|
1900
|
+
);
|
|
1901
|
+
const removedPaths = [];
|
|
1902
|
+
for (const entry of fs.readdirSync(lanePaths.executorOverlaysDir, { withFileTypes: true })) {
|
|
1903
|
+
if (!entry.isDirectory() || !/^wave-\d+$/.test(entry.name)) {
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
const waveNumber = Number.parseInt(entry.name.slice("wave-".length), 10);
|
|
1907
|
+
const waveDir = path.join(lanePaths.executorOverlaysDir, entry.name);
|
|
1908
|
+
const expectedSlugs = expectedSlugsByWave.get(waveNumber);
|
|
1909
|
+
if (!expectedSlugs) {
|
|
1910
|
+
fs.rmSync(waveDir, { recursive: true, force: true });
|
|
1911
|
+
removedPaths.push(path.relative(REPO_ROOT, waveDir));
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
for (const child of fs.readdirSync(waveDir, { withFileTypes: true })) {
|
|
1915
|
+
if (!child.isDirectory() || expectedSlugs.has(child.name)) {
|
|
1916
|
+
continue;
|
|
1917
|
+
}
|
|
1918
|
+
const childPath = path.join(waveDir, child.name);
|
|
1919
|
+
fs.rmSync(childPath, { recursive: true, force: true });
|
|
1920
|
+
removedPaths.push(path.relative(REPO_ROOT, childPath));
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return removedPaths.toSorted();
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1802
1926
|
export function reconcileStaleLauncherArtifacts(lanePaths, options = {}) {
|
|
1803
1927
|
const outcome = {
|
|
1804
1928
|
removedLock: false,
|
|
@@ -2162,9 +2286,63 @@ function relaunchReasonBuckets(runs, failures, derivedState) {
|
|
|
2162
2286
|
closureGate: (failures || []).some(
|
|
2163
2287
|
(failure) => failure.agentId && selectedAgentIds.has(failure.agentId),
|
|
2164
2288
|
),
|
|
2289
|
+
sharedComponentSiblingWait: (failures || []).some(
|
|
2290
|
+
(failure) =>
|
|
2291
|
+
failure.statusCode === "shared-component-sibling-pending" &&
|
|
2292
|
+
(failure.waitingOnAgentIds || []).some((agentId) => selectedAgentIds.has(agentId)),
|
|
2293
|
+
),
|
|
2165
2294
|
};
|
|
2166
2295
|
}
|
|
2167
2296
|
|
|
2297
|
+
function applySharedComponentWaitStateToDashboard(componentGate, dashboardState) {
|
|
2298
|
+
const waitingSummary = (componentGate?.waitingOnAgentIds || []).join("/");
|
|
2299
|
+
if (!waitingSummary) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
for (const agentId of componentGate?.satisfiedAgentIds || []) {
|
|
2303
|
+
setWaveDashboardAgent(dashboardState, agentId, {
|
|
2304
|
+
state: "completed",
|
|
2305
|
+
detail: `Desired-state slice landed; waiting on ${waitingSummary} for shared component closure`,
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
function reconcileFailuresAgainstSharedComponentState(wave, agentRuns, failures) {
|
|
2311
|
+
if (!Array.isArray(failures) || failures.length === 0) {
|
|
2312
|
+
return failures;
|
|
2313
|
+
}
|
|
2314
|
+
const summariesByAgentId = Object.fromEntries(
|
|
2315
|
+
(agentRuns || []).map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
2316
|
+
);
|
|
2317
|
+
const failureAgentIds = new Set(failures.map((failure) => failure.agentId).filter(Boolean));
|
|
2318
|
+
const consumedSatisfiedAgentIds = new Set();
|
|
2319
|
+
const synthesizedFailures = [];
|
|
2320
|
+
for (const promotion of wave?.componentPromotions || []) {
|
|
2321
|
+
const componentState = analyzePromotedComponentOwners(
|
|
2322
|
+
promotion.componentId,
|
|
2323
|
+
agentRuns,
|
|
2324
|
+
summariesByAgentId,
|
|
2325
|
+
);
|
|
2326
|
+
if (
|
|
2327
|
+
componentState.satisfiedAgentIds.length === 0 ||
|
|
2328
|
+
componentState.waitingOnAgentIds.length === 0 ||
|
|
2329
|
+
!componentState.satisfiedAgentIds.some((agentId) => failureAgentIds.has(agentId))
|
|
2330
|
+
) {
|
|
2331
|
+
continue;
|
|
2332
|
+
}
|
|
2333
|
+
for (const agentId of componentState.satisfiedAgentIds) {
|
|
2334
|
+
if (failureAgentIds.has(agentId)) {
|
|
2335
|
+
consumedSatisfiedAgentIds.add(agentId);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
synthesizedFailures.push(buildSharedComponentSiblingPendingFailure(componentState));
|
|
2339
|
+
}
|
|
2340
|
+
return [
|
|
2341
|
+
...synthesizedFailures.filter(Boolean),
|
|
2342
|
+
...failures.filter((failure) => !consumedSatisfiedAgentIds.has(failure.agentId)),
|
|
2343
|
+
];
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2168
2346
|
export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
2169
2347
|
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
2170
2348
|
const basicReuseOk = Boolean(
|
|
@@ -2272,7 +2450,11 @@ function buildFallbackExecutorState(executorState, executorId, attempt, reason)
|
|
|
2272
2450
|
}
|
|
2273
2451
|
|
|
2274
2452
|
function applyRetryFallbacks(agentRuns, failures, lanePaths, attemptNumber, waveDefinition = null) {
|
|
2275
|
-
const failedAgentIds = new Set(
|
|
2453
|
+
const failedAgentIds = new Set(
|
|
2454
|
+
failures
|
|
2455
|
+
.filter((failure) => failure.statusCode !== "shared-component-sibling-pending")
|
|
2456
|
+
.map((failure) => failure.agentId),
|
|
2457
|
+
);
|
|
2276
2458
|
let changed = false;
|
|
2277
2459
|
const outcomes = new Map();
|
|
2278
2460
|
for (const run of agentRuns) {
|
|
@@ -2733,6 +2915,20 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
|
|
|
2733
2915
|
barrier: null,
|
|
2734
2916
|
};
|
|
2735
2917
|
}
|
|
2918
|
+
const sharedComponentWaitingAgentIds = new Set(
|
|
2919
|
+
(failures || [])
|
|
2920
|
+
.filter((failure) => failure.statusCode === "shared-component-sibling-pending")
|
|
2921
|
+
.flatMap((failure) => failure.waitingOnAgentIds || [])
|
|
2922
|
+
.filter((agentId) => runsByAgentId.has(agentId)),
|
|
2923
|
+
);
|
|
2924
|
+
if (sharedComponentWaitingAgentIds.size > 0) {
|
|
2925
|
+
return {
|
|
2926
|
+
runs: Array.from(sharedComponentWaitingAgentIds)
|
|
2927
|
+
.map((agentId) => runsByAgentId.get(agentId))
|
|
2928
|
+
.filter(Boolean),
|
|
2929
|
+
barrier: null,
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2736
2932
|
const failedAgentIds = new Set(failures.map((failure) => failure.agentId));
|
|
2737
2933
|
return {
|
|
2738
2934
|
runs: agentRuns.filter((run) => failedAgentIds.has(run.agent.agentId)),
|
|
@@ -2779,6 +2975,8 @@ export async function runLauncherCli(argv) {
|
|
|
2779
2975
|
let globalDashboard = null;
|
|
2780
2976
|
let globalDashboardTerminalEntry = null;
|
|
2781
2977
|
let globalDashboardTerminalAppended = false;
|
|
2978
|
+
let currentWaveDashboardTerminalEntry = null;
|
|
2979
|
+
let currentWaveDashboardTerminalAppended = false;
|
|
2782
2980
|
let selectedWavesForCoordination = [];
|
|
2783
2981
|
|
|
2784
2982
|
const appendCoordination = ({
|
|
@@ -2976,6 +3174,7 @@ export async function runLauncherCli(argv) {
|
|
|
2976
3174
|
});
|
|
2977
3175
|
|
|
2978
3176
|
if (options.dryRun) {
|
|
3177
|
+
pruneDryRunExecutorPreviewDirs(lanePaths, allWaves);
|
|
2979
3178
|
for (const wave of filteredWaves) {
|
|
2980
3179
|
const derivedState = writeWaveDerivedState({
|
|
2981
3180
|
lanePaths,
|
|
@@ -3072,9 +3271,14 @@ export async function runLauncherCli(argv) {
|
|
|
3072
3271
|
lanePaths,
|
|
3073
3272
|
globalDashboard.runId || "global",
|
|
3074
3273
|
);
|
|
3274
|
+
currentWaveDashboardTerminalEntry = createCurrentWaveDashboardTerminalEntry(lanePaths);
|
|
3075
3275
|
if (terminalRegistryEnabled) {
|
|
3076
|
-
appendTerminalEntries(lanePaths.terminalsPath, [
|
|
3276
|
+
appendTerminalEntries(lanePaths.terminalsPath, [
|
|
3277
|
+
globalDashboardTerminalEntry,
|
|
3278
|
+
currentWaveDashboardTerminalEntry,
|
|
3279
|
+
]);
|
|
3077
3280
|
globalDashboardTerminalAppended = true;
|
|
3281
|
+
currentWaveDashboardTerminalAppended = true;
|
|
3078
3282
|
}
|
|
3079
3283
|
launchWaveDashboardSession(lanePaths, {
|
|
3080
3284
|
sessionName: globalDashboardTerminalEntry.sessionName,
|
|
@@ -3152,7 +3356,7 @@ export async function runLauncherCli(argv) {
|
|
|
3152
3356
|
wave.wave,
|
|
3153
3357
|
wave.agents,
|
|
3154
3358
|
runTag,
|
|
3155
|
-
|
|
3359
|
+
false,
|
|
3156
3360
|
);
|
|
3157
3361
|
if (terminalRegistryEnabled) {
|
|
3158
3362
|
appendTerminalEntries(lanePaths.terminalsPath, terminalEntries);
|
|
@@ -3265,12 +3469,9 @@ export async function runLauncherCli(argv) {
|
|
|
3265
3469
|
}
|
|
3266
3470
|
flushDashboards();
|
|
3267
3471
|
|
|
3268
|
-
|
|
3269
|
-
(entry) => entry.terminalName === `${lanePaths.dashboardTerminalNamePrefix}${wave.wave}`,
|
|
3270
|
-
);
|
|
3271
|
-
if (options.dashboard && dashboardEntry) {
|
|
3472
|
+
if (options.dashboard && currentWaveDashboardTerminalEntry) {
|
|
3272
3473
|
launchWaveDashboardSession(lanePaths, {
|
|
3273
|
-
sessionName:
|
|
3474
|
+
sessionName: currentWaveDashboardTerminalEntry.sessionName,
|
|
3274
3475
|
dashboardPath,
|
|
3275
3476
|
messageBoardPath,
|
|
3276
3477
|
});
|
|
@@ -3442,6 +3643,12 @@ export async function runLauncherCli(argv) {
|
|
|
3442
3643
|
|
|
3443
3644
|
materializeAgentExecutionSummaries(wave, agentRuns);
|
|
3444
3645
|
refreshDerivedState(attempt);
|
|
3646
|
+
failures = reconcileFailuresAgainstSharedComponentState(wave, agentRuns, failures);
|
|
3647
|
+
for (const failure of failures) {
|
|
3648
|
+
if (failure.statusCode === "shared-component-sibling-pending") {
|
|
3649
|
+
applySharedComponentWaitStateToDashboard(failure, dashboardState);
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3445
3652
|
|
|
3446
3653
|
if (failures.length > 0) {
|
|
3447
3654
|
for (const failure of failures) {
|
|
@@ -3483,12 +3690,20 @@ export async function runLauncherCli(argv) {
|
|
|
3483
3690
|
laneProfile: lanePaths.laneProfile,
|
|
3484
3691
|
});
|
|
3485
3692
|
if (!componentGate.ok) {
|
|
3693
|
+
if (componentGate.statusCode === "shared-component-sibling-pending") {
|
|
3694
|
+
applySharedComponentWaitStateToDashboard(componentGate, dashboardState);
|
|
3695
|
+
}
|
|
3486
3696
|
failures = [
|
|
3487
3697
|
{
|
|
3488
3698
|
agentId: componentGate.agentId,
|
|
3489
3699
|
statusCode: componentGate.statusCode,
|
|
3490
3700
|
logPath:
|
|
3491
3701
|
componentGate.logPath || path.relative(REPO_ROOT, messageBoardPath),
|
|
3702
|
+
detail: componentGate.detail,
|
|
3703
|
+
ownerAgentIds: componentGate.ownerAgentIds || [],
|
|
3704
|
+
satisfiedAgentIds: componentGate.satisfiedAgentIds || [],
|
|
3705
|
+
waitingOnAgentIds: componentGate.waitingOnAgentIds || [],
|
|
3706
|
+
failedOwnContractAgentIds: componentGate.failedOwnContractAgentIds || [],
|
|
3492
3707
|
},
|
|
3493
3708
|
];
|
|
3494
3709
|
recordCombinedEvent({
|
|
@@ -4045,6 +4260,9 @@ export async function runLauncherCli(argv) {
|
|
|
4045
4260
|
if (globalDashboardTerminalEntry) {
|
|
4046
4261
|
excludeSessionNames.add(globalDashboardTerminalEntry.sessionName);
|
|
4047
4262
|
}
|
|
4263
|
+
if (currentWaveDashboardTerminalEntry) {
|
|
4264
|
+
excludeSessionNames.add(currentWaveDashboardTerminalEntry.sessionName);
|
|
4265
|
+
}
|
|
4048
4266
|
cleanupLaneTmuxSessions(lanePaths, { excludeSessionNames });
|
|
4049
4267
|
}
|
|
4050
4268
|
if (globalWave && globalWave.status === "running") {
|
|
@@ -4079,6 +4297,13 @@ export async function runLauncherCli(argv) {
|
|
|
4079
4297
|
if (globalDashboardTerminalAppended && globalDashboardTerminalEntry && !options.keepTerminals) {
|
|
4080
4298
|
removeTerminalEntries(lanePaths.terminalsPath, [globalDashboardTerminalEntry]);
|
|
4081
4299
|
}
|
|
4300
|
+
if (
|
|
4301
|
+
currentWaveDashboardTerminalAppended &&
|
|
4302
|
+
currentWaveDashboardTerminalEntry &&
|
|
4303
|
+
!options.keepTerminals
|
|
4304
|
+
) {
|
|
4305
|
+
removeTerminalEntries(lanePaths.terminalsPath, [currentWaveDashboardTerminalEntry]);
|
|
4306
|
+
}
|
|
4082
4307
|
if (options.cleanupSessions && globalDashboardTerminalEntry) {
|
|
4083
4308
|
try {
|
|
4084
4309
|
killTmuxSessionIfExists(lanePaths.tmuxSocketName, globalDashboardTerminalEntry.sessionName);
|
|
@@ -4086,6 +4311,16 @@ export async function runLauncherCli(argv) {
|
|
|
4086
4311
|
// no-op
|
|
4087
4312
|
}
|
|
4088
4313
|
}
|
|
4314
|
+
if (options.cleanupSessions && currentWaveDashboardTerminalEntry) {
|
|
4315
|
+
try {
|
|
4316
|
+
killTmuxSessionIfExists(
|
|
4317
|
+
lanePaths.tmuxSocketName,
|
|
4318
|
+
currentWaveDashboardTerminalEntry.sessionName,
|
|
4319
|
+
);
|
|
4320
|
+
} catch {
|
|
4321
|
+
// no-op
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4089
4324
|
if (lockHeld) {
|
|
4090
4325
|
releaseLauncherLock(lanePaths.launcherLockPath);
|
|
4091
4326
|
}
|
|
@@ -61,11 +61,18 @@ export function writeTerminalsConfig(filePath, config) {
|
|
|
61
61
|
function isLaneTemporaryTerminalName(name, lanePaths) {
|
|
62
62
|
return (
|
|
63
63
|
name === lanePaths.globalDashboardTerminalName ||
|
|
64
|
+
name === currentWaveDashboardTerminalName(lanePaths) ||
|
|
64
65
|
name.startsWith(lanePaths.terminalNamePrefix) ||
|
|
65
66
|
name.startsWith(lanePaths.dashboardTerminalNamePrefix)
|
|
66
67
|
);
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function currentWaveDashboardTerminalName(lanePaths) {
|
|
71
|
+
return lanePaths.lane === "main"
|
|
72
|
+
? "Current Wave Dashboard"
|
|
73
|
+
: `Current Wave Dashboard (${lanePaths.lane})`;
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
function extractTmuxSessionName(command, socketName) {
|
|
70
77
|
const text = String(command || "").trim();
|
|
71
78
|
const marker = `tmux -L ${socketName} new -As `;
|
|
@@ -138,6 +145,24 @@ export function createGlobalDashboardTerminalEntry(lanePaths, runTag) {
|
|
|
138
145
|
};
|
|
139
146
|
}
|
|
140
147
|
|
|
148
|
+
export function createCurrentWaveDashboardTerminalEntry(lanePaths) {
|
|
149
|
+
const sessionName = `${lanePaths.tmuxDashboardSessionPrefix}_current`.replace(
|
|
150
|
+
/[^a-zA-Z0-9:_-]/g,
|
|
151
|
+
"_",
|
|
152
|
+
);
|
|
153
|
+
const terminalName = currentWaveDashboardTerminalName(lanePaths);
|
|
154
|
+
return {
|
|
155
|
+
terminalName,
|
|
156
|
+
sessionName,
|
|
157
|
+
config: {
|
|
158
|
+
name: terminalName,
|
|
159
|
+
icon: DASHBOARD_TERMINAL_ICON,
|
|
160
|
+
color: DASHBOARD_TERMINAL_COLOR,
|
|
161
|
+
command: `TMUX= tmux -L ${lanePaths.tmuxSocketName} new -As ${sessionName}`,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
141
166
|
export function appendTerminalEntries(terminalsPath, entries) {
|
|
142
167
|
const config = readTerminalsConfig(terminalsPath);
|
|
143
168
|
const namesToReplace = new Set(entries.map((entry) => entry.terminalName));
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
DEFAULT_INTEGRATION_ROLE_PROMPT_PATH,
|
|
14
14
|
DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
15
15
|
DEFAULT_WAVE_LANE,
|
|
16
|
+
normalizeClaudeEffort,
|
|
16
17
|
loadWaveConfig,
|
|
17
18
|
normalizeCodexSandboxMode,
|
|
18
19
|
normalizeExecutorMode,
|
|
@@ -700,6 +701,7 @@ export function normalizeAgentExecutorConfig(rawSettings, filePath, label) {
|
|
|
700
701
|
"claude.agent",
|
|
701
702
|
"claude.permission_mode",
|
|
702
703
|
"claude.permission_prompt_tool",
|
|
704
|
+
"claude.effort",
|
|
703
705
|
"claude.max_turns",
|
|
704
706
|
"claude.mcp_config",
|
|
705
707
|
"claude.settings",
|
|
@@ -827,6 +829,11 @@ export function normalizeAgentExecutorConfig(rawSettings, filePath, label) {
|
|
|
827
829
|
...(executorConfig.claude || {}),
|
|
828
830
|
permissionPromptTool: value,
|
|
829
831
|
};
|
|
832
|
+
} else if (key === "claude.effort") {
|
|
833
|
+
executorConfig.claude = {
|
|
834
|
+
...(executorConfig.claude || {}),
|
|
835
|
+
effort: normalizeClaudeEffort(value, `${label}.claude.effort`),
|
|
836
|
+
};
|
|
830
837
|
} else if (key === "claude.max_turns") {
|
|
831
838
|
executorConfig.claude = {
|
|
832
839
|
...(executorConfig.claude || {}),
|
|
@@ -2038,6 +2045,28 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2038
2045
|
profile?.budget?.minutes ??
|
|
2039
2046
|
null,
|
|
2040
2047
|
};
|
|
2048
|
+
const claudeMaxTurnsSource =
|
|
2049
|
+
executorConfig?.claude?.maxTurns !== null && executorConfig?.claude?.maxTurns !== undefined
|
|
2050
|
+
? "claude.maxTurns"
|
|
2051
|
+
: profile?.claude?.maxTurns !== null && profile?.claude?.maxTurns !== undefined
|
|
2052
|
+
? "claude.maxTurns"
|
|
2053
|
+
: runtimeBudget.turns !== null
|
|
2054
|
+
? "budget.turns"
|
|
2055
|
+
: laneProfile.executors.claude.maxTurns !== null &&
|
|
2056
|
+
laneProfile.executors.claude.maxTurns !== undefined
|
|
2057
|
+
? "claude.maxTurns"
|
|
2058
|
+
: null;
|
|
2059
|
+
const opencodeStepsSource =
|
|
2060
|
+
executorConfig?.opencode?.steps !== null && executorConfig?.opencode?.steps !== undefined
|
|
2061
|
+
? "opencode.steps"
|
|
2062
|
+
: profile?.opencode?.steps !== null && profile?.opencode?.steps !== undefined
|
|
2063
|
+
? "opencode.steps"
|
|
2064
|
+
: runtimeBudget.turns !== null
|
|
2065
|
+
? "budget.turns"
|
|
2066
|
+
: laneProfile.executors.opencode.steps !== null &&
|
|
2067
|
+
laneProfile.executors.opencode.steps !== undefined
|
|
2068
|
+
? "opencode.steps"
|
|
2069
|
+
: null;
|
|
2041
2070
|
return {
|
|
2042
2071
|
id: executorId,
|
|
2043
2072
|
initialExecutorId: executorId,
|
|
@@ -2122,6 +2151,7 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2122
2151
|
profile?.claude?.maxTurns ??
|
|
2123
2152
|
runtimeBudget.turns ??
|
|
2124
2153
|
laneProfile.executors.claude.maxTurns,
|
|
2154
|
+
maxTurnsSource: claudeMaxTurnsSource,
|
|
2125
2155
|
},
|
|
2126
2156
|
opencode: {
|
|
2127
2157
|
...mergeExecutorSections(
|
|
@@ -2139,6 +2169,7 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2139
2169
|
profile?.opencode?.steps ??
|
|
2140
2170
|
runtimeBudget.turns ??
|
|
2141
2171
|
laneProfile.executors.opencode.steps,
|
|
2172
|
+
stepsSource: opencodeStepsSource,
|
|
2142
2173
|
},
|
|
2143
2174
|
};
|
|
2144
2175
|
}
|