@chllming/wave-orchestration 0.5.2 → 0.5.4

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 (79) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +56 -501
  3. package/docs/README.md +39 -0
  4. package/docs/concepts/context7-vs-skills.md +94 -0
  5. package/docs/concepts/operating-modes.md +91 -0
  6. package/docs/concepts/runtime-agnostic-orchestration.md +95 -0
  7. package/docs/concepts/what-is-a-wave.md +133 -0
  8. package/docs/guides/planner.md +113 -0
  9. package/docs/guides/terminal-surfaces.md +80 -0
  10. package/docs/image.png +0 -0
  11. package/docs/plans/context7-wave-orchestrator.md +2 -0
  12. package/docs/plans/current-state.md +10 -0
  13. package/docs/plans/master-plan.md +3 -3
  14. package/docs/plans/migration.md +4 -3
  15. package/docs/plans/wave-orchestrator.md +27 -3
  16. package/docs/reference/runtime-config/README.md +19 -0
  17. package/docs/reference/skills.md +156 -0
  18. package/docs/roadmap.md +160 -564
  19. package/package.json +2 -1
  20. package/releases/manifest.json +32 -0
  21. package/scripts/wave-orchestrator/config.mjs +17 -0
  22. package/scripts/wave-orchestrator/context7.mjs +9 -0
  23. package/scripts/wave-orchestrator/coordination.mjs +16 -0
  24. package/scripts/wave-orchestrator/executors.mjs +24 -11
  25. package/scripts/wave-orchestrator/install.mjs +41 -2
  26. package/scripts/wave-orchestrator/launcher.mjs +131 -25
  27. package/scripts/wave-orchestrator/planner.mjs +1328 -0
  28. package/scripts/wave-orchestrator/project-profile.mjs +190 -0
  29. package/scripts/wave-orchestrator/shared.mjs +2 -0
  30. package/scripts/wave-orchestrator/skills.mjs +448 -0
  31. package/scripts/wave-orchestrator/terminals.mjs +16 -0
  32. package/scripts/wave-orchestrator/traces.mjs +23 -0
  33. package/scripts/wave-orchestrator/wave-files.mjs +299 -84
  34. package/scripts/wave.mjs +11 -0
  35. package/skills/provider-aws/SKILL.md +6 -0
  36. package/skills/provider-aws/skill.json +5 -0
  37. package/skills/provider-custom-deploy/SKILL.md +5 -0
  38. package/skills/provider-custom-deploy/skill.json +5 -0
  39. package/skills/provider-docker-compose/SKILL.md +6 -0
  40. package/skills/provider-docker-compose/skill.json +5 -0
  41. package/skills/provider-github-release/SKILL.md +6 -0
  42. package/skills/provider-github-release/skill.json +5 -0
  43. package/skills/provider-kubernetes/SKILL.md +6 -0
  44. package/skills/provider-kubernetes/skill.json +5 -0
  45. package/skills/provider-railway/SKILL.md +6 -0
  46. package/skills/provider-railway/adapters/claude.md +1 -0
  47. package/skills/provider-railway/adapters/codex.md +1 -0
  48. package/skills/provider-railway/adapters/local.md +1 -0
  49. package/skills/provider-railway/adapters/opencode.md +1 -0
  50. package/skills/provider-railway/skill.json +5 -0
  51. package/skills/provider-ssh-manual/SKILL.md +6 -0
  52. package/skills/provider-ssh-manual/skill.json +5 -0
  53. package/skills/repo-coding-rules/SKILL.md +7 -0
  54. package/skills/repo-coding-rules/skill.json +5 -0
  55. package/skills/role-deploy/SKILL.md +6 -0
  56. package/skills/role-deploy/skill.json +5 -0
  57. package/skills/role-documentation/SKILL.md +6 -0
  58. package/skills/role-documentation/skill.json +5 -0
  59. package/skills/role-evaluator/SKILL.md +6 -0
  60. package/skills/role-evaluator/skill.json +5 -0
  61. package/skills/role-implementation/SKILL.md +6 -0
  62. package/skills/role-implementation/skill.json +5 -0
  63. package/skills/role-infra/SKILL.md +6 -0
  64. package/skills/role-infra/skill.json +5 -0
  65. package/skills/role-integration/SKILL.md +6 -0
  66. package/skills/role-integration/skill.json +5 -0
  67. package/skills/role-research/SKILL.md +6 -0
  68. package/skills/role-research/skill.json +5 -0
  69. package/skills/runtime-claude/SKILL.md +6 -0
  70. package/skills/runtime-claude/skill.json +5 -0
  71. package/skills/runtime-codex/SKILL.md +6 -0
  72. package/skills/runtime-codex/skill.json +5 -0
  73. package/skills/runtime-local/SKILL.md +5 -0
  74. package/skills/runtime-local/skill.json +5 -0
  75. package/skills/runtime-opencode/SKILL.md +6 -0
  76. package/skills/runtime-opencode/skill.json +5 -0
  77. package/skills/wave-core/SKILL.md +7 -0
  78. package/skills/wave-core/skill.json +5 -0
  79. package/wave.config.json +27 -0
@@ -87,9 +87,12 @@ import {
87
87
  createGlobalDashboardTerminalEntry,
88
88
  createTemporaryTerminalEntries,
89
89
  killTmuxSessionIfExists,
90
+ normalizeTerminalSurface,
90
91
  pruneOrphanLaneTemporaryTerminalEntries,
91
92
  removeLaneTemporaryTerminalEntries,
92
93
  removeTerminalEntries,
94
+ terminalSurfaceUsesTerminalRegistry,
95
+ TERMINAL_SURFACES,
93
96
  } from "./terminals.mjs";
94
97
  import {
95
98
  buildCodexExecInvocation,
@@ -124,6 +127,12 @@ import { buildDocsQueue, readDocsQueue, writeDocsQueue } from "./docs-queue.mjs"
124
127
  import { deriveWaveLedger, readWaveLedger, writeWaveLedger } from "./ledger.mjs";
125
128
  import { buildQualityMetrics, writeTraceBundle } from "./traces.mjs";
126
129
  import { triageClarificationRequests } from "./clarification-triage.mjs";
130
+ import { readProjectProfile, resolveDefaultTerminalSurface } from "./project-profile.mjs";
131
+ import {
132
+ resolveAgentSkills,
133
+ summarizeResolvedSkills,
134
+ writeResolvedSkillArtifacts,
135
+ } from "./skills.mjs";
127
136
  import {
128
137
  buildDependencySnapshot,
129
138
  buildRequestAssignments,
@@ -133,7 +142,22 @@ import {
133
142
  } from "./routing-state.mjs";
134
143
  export { CODEX_SANDBOX_MODES, DEFAULT_CODEX_SANDBOX_MODE, normalizeCodexSandboxMode, buildCodexExecInvocation };
135
144
 
136
- function printUsage(lanePaths) {
145
+ export function formatReconcileBlockedWaveLine(blockedWave) {
146
+ const parts = Array.isArray(blockedWave?.reasons)
147
+ ? blockedWave.reasons
148
+ .map((reason) => {
149
+ const code = compactSingleLine(reason?.code || "", 80);
150
+ const detail = compactSingleLine(reason?.detail || "", 240);
151
+ return code && detail ? `${code}=${detail}` : "";
152
+ })
153
+ .filter(Boolean)
154
+ : [];
155
+ return `[reconcile] wave ${blockedWave?.wave ?? "unknown"} not reconstructable: ${
156
+ parts.join("; ") || "unknown reason"
157
+ }`;
158
+ }
159
+
160
+ function printUsage(lanePaths, terminalSurface) {
137
161
  console.log(`Usage: pnpm exec wave launch [options]
138
162
 
139
163
  Options:
@@ -158,6 +182,8 @@ Options:
158
182
  --codex-sandbox <mode> Codex sandbox mode: ${CODEX_SANDBOX_MODES.join(" | ")} (default: ${DEFAULT_CODEX_SANDBOX_MODE})
159
183
  --manifest-out <path> Write parsed wave manifest JSON (default: ${path.relative(REPO_ROOT, lanePaths.defaultManifestPath)})
160
184
  --dry-run Parse waves and update manifest only
185
+ --terminal-surface <mode>
186
+ Terminal surface: ${TERMINAL_SURFACES.join(" | ")} (default: ${terminalSurface})
161
187
  --no-dashboard Disable per-wave tmux dashboard session
162
188
  --cleanup-sessions Kill lane tmux sessions after each wave (default: on)
163
189
  --keep-sessions Keep lane tmux sessions after each wave
@@ -176,6 +202,7 @@ Options:
176
202
 
177
203
  function parseArgs(argv) {
178
204
  let lanePaths = buildLanePaths(DEFAULT_WAVE_LANE);
205
+ const projectProfile = readProjectProfile();
179
206
  const options = {
180
207
  lane: DEFAULT_WAVE_LANE,
181
208
  startWave: 0,
@@ -193,6 +220,7 @@ function parseArgs(argv) {
193
220
  codexSandboxMode: DEFAULT_CODEX_SANDBOX_MODE,
194
221
  manifestOut: lanePaths.defaultManifestPath,
195
222
  dryRun: false,
223
+ terminalSurface: resolveDefaultTerminalSurface(projectProfile),
196
224
  dashboard: true,
197
225
  cleanupSessions: true,
198
226
  keepTerminals: false,
@@ -216,6 +244,8 @@ function parseArgs(argv) {
216
244
  }
217
245
  if (arg === "--dry-run") {
218
246
  options.dryRun = true;
247
+ } else if (arg === "--terminal-surface") {
248
+ options.terminalSurface = normalizeTerminalSurface(argv[++i], "--terminal-surface");
219
249
  } else if (arg === "--no-dashboard") {
220
250
  options.dashboard = false;
221
251
  } else if (arg === "--cleanup-sessions") {
@@ -305,6 +335,9 @@ function parseArgs(argv) {
305
335
  if (!options.autoNext && options.endWave !== null && options.endWave < options.startWave) {
306
336
  throw new Error("--end-wave must be >= --start-wave");
307
337
  }
338
+ if (!options.dryRun && options.terminalSurface === "none") {
339
+ throw new Error("--terminal-surface none is only supported with --dry-run");
340
+ }
308
341
  return { help: false, lanePaths, options };
309
342
  }
310
343
 
@@ -1336,6 +1369,7 @@ export async function runClosureSweepPhase({
1336
1369
  flushDashboards();
1337
1370
  const launchResult = await launchAgentSessionFn(lanePaths, {
1338
1371
  wave: wave.wave,
1372
+ waveDefinition: wave,
1339
1373
  agent: runInfo.agent,
1340
1374
  sessionName: runInfo.sessionName,
1341
1375
  promptPath: runInfo.promptPath,
@@ -1359,6 +1393,7 @@ export async function runClosureSweepPhase({
1359
1393
  runInfo.lastPromptHash = launchResult?.promptHash || null;
1360
1394
  runInfo.lastContext7 = launchResult?.context7 || null;
1361
1395
  runInfo.lastExecutorId = launchResult?.executorId || runInfo.agent.executorResolved?.id || null;
1396
+ runInfo.lastSkillProjection = launchResult?.skills || summarizeResolvedSkills(runInfo.agent.skillsResolved);
1362
1397
  setWaveDashboardAgent(dashboardState, runInfo.agent.agentId, {
1363
1398
  state: "running",
1364
1399
  detail: `Closure sweep launched${launchResult?.context7?.mode ? ` (${launchResult.context7.mode})` : ""}`,
@@ -1573,7 +1608,7 @@ function removeOrphanWaveDashboards(lanePaths, activeSessionNames) {
1573
1608
  return removedDashboardPaths;
1574
1609
  }
1575
1610
 
1576
- export function reconcileStaleLauncherArtifacts(lanePaths) {
1611
+ export function reconcileStaleLauncherArtifacts(lanePaths, options = {}) {
1577
1612
  const outcome = {
1578
1613
  removedLock: false,
1579
1614
  removedSessions: [],
@@ -1597,12 +1632,14 @@ export function reconcileStaleLauncherArtifacts(lanePaths) {
1597
1632
 
1598
1633
  outcome.removedSessions = cleanupLaneTmuxSessions(lanePaths);
1599
1634
  const activeSessionNames = new Set(listLaneTmuxSessionNames(lanePaths));
1600
- const terminalCleanup = pruneOrphanLaneTemporaryTerminalEntries(
1601
- lanePaths.terminalsPath,
1602
- lanePaths,
1603
- activeSessionNames,
1604
- );
1605
- outcome.removedTerminalNames = terminalCleanup.removedNames;
1635
+ if (terminalSurfaceUsesTerminalRegistry(options.terminalSurface || "vscode")) {
1636
+ const terminalCleanup = pruneOrphanLaneTemporaryTerminalEntries(
1637
+ lanePaths.terminalsPath,
1638
+ lanePaths,
1639
+ activeSessionNames,
1640
+ );
1641
+ outcome.removedTerminalNames = terminalCleanup.removedNames;
1642
+ }
1606
1643
 
1607
1644
  const globalDashboard = readJsonOrNull(lanePaths.globalDashboardPath);
1608
1645
  if (globalDashboard && typeof globalDashboard === "object" && Array.isArray(globalDashboard.waves)) {
@@ -1740,9 +1777,19 @@ function launchWaveDashboardSession(lanePaths, { sessionName, dashboardPath, mes
1740
1777
  );
1741
1778
  }
1742
1779
 
1780
+ function refreshResolvedSkillsForRun(runInfo, waveDefinition, lanePaths) {
1781
+ runInfo.agent.skillsResolved = resolveAgentSkills(
1782
+ runInfo.agent,
1783
+ waveDefinition || { deployEnvironments: [] },
1784
+ { laneProfile: lanePaths.laneProfile },
1785
+ );
1786
+ return runInfo.agent.skillsResolved;
1787
+ }
1788
+
1743
1789
  async function launchAgentSession(lanePaths, params) {
1744
1790
  const {
1745
1791
  wave,
1792
+ waveDefinition = null,
1746
1793
  agent,
1747
1794
  sessionName,
1748
1795
  promptPath,
@@ -1770,6 +1817,20 @@ async function launchAgentSession(lanePaths, params) {
1770
1817
  cacheDir: lanePaths.context7CacheDir,
1771
1818
  disabled: !context7Enabled,
1772
1819
  });
1820
+ const overlayDir = path.join(lanePaths.executorOverlaysDir, `wave-${wave}`, agent.slug);
1821
+ ensureDirectory(overlayDir);
1822
+ const skillsResolved =
1823
+ agent.skillsResolved || resolveAgentSkills(agent, waveDefinition || { deployEnvironments: [] }, {
1824
+ laneProfile: lanePaths.laneProfile,
1825
+ });
1826
+ agent.skillsResolved = skillsResolved;
1827
+ const skillArtifacts = writeResolvedSkillArtifacts(overlayDir, skillsResolved);
1828
+ if (skillArtifacts) {
1829
+ agent.skillsResolved = {
1830
+ ...skillsResolved,
1831
+ artifacts: skillArtifacts,
1832
+ };
1833
+ }
1773
1834
  const prompt = buildExecutionPrompt({
1774
1835
  lane: lanePaths.lane,
1775
1836
  wave,
@@ -1790,12 +1851,12 @@ async function launchAgentSession(lanePaths, params) {
1790
1851
  });
1791
1852
  const promptHash = hashAgentPromptFingerprint(agent);
1792
1853
  fs.writeFileSync(promptPath, `${prompt}\n`, "utf8");
1793
- const overlayDir = path.join(lanePaths.executorOverlaysDir, `wave-${wave}`, agent.slug);
1794
1854
  const launchSpec = buildExecutorLaunchSpec({
1795
1855
  agent,
1796
1856
  promptPath,
1797
1857
  logPath,
1798
1858
  overlayDir,
1859
+ skillProjection: agent.skillsResolved,
1799
1860
  });
1800
1861
  const resolvedExecutorMode = launchSpec.executorId || agent.executorResolved?.id || "codex";
1801
1862
  if (dryRun) {
@@ -1805,8 +1866,16 @@ async function launchAgentSession(lanePaths, params) {
1805
1866
  env: launchSpec.env || {},
1806
1867
  useRateLimitRetries: launchSpec.useRateLimitRetries === true,
1807
1868
  invocationLines: launchSpec.invocationLines,
1869
+ skills: summarizeResolvedSkills(agent.skillsResolved),
1808
1870
  });
1809
- return { promptHash, context7, executorId: resolvedExecutorMode, launchSpec, dryRun: true };
1871
+ return {
1872
+ promptHash,
1873
+ context7,
1874
+ executorId: resolvedExecutorMode,
1875
+ launchSpec,
1876
+ dryRun: true,
1877
+ skills: summarizeResolvedSkills(agent.skillsResolved),
1878
+ };
1810
1879
  }
1811
1880
  killTmuxSessionIfExists(lanePaths.tmuxSocketName, sessionName);
1812
1881
 
@@ -1881,7 +1950,12 @@ async function launchAgentSession(lanePaths, params) {
1881
1950
  ["new-session", "-d", "-s", sessionName, `bash -lc ${shellQuote(command)}`],
1882
1951
  `launch session ${sessionName}`,
1883
1952
  );
1884
- return { promptHash, context7, executorId: resolvedExecutorMode };
1953
+ return {
1954
+ promptHash,
1955
+ context7,
1956
+ executorId: resolvedExecutorMode,
1957
+ skills: summarizeResolvedSkills(agent.skillsResolved),
1958
+ };
1885
1959
  }
1886
1960
 
1887
1961
  async function waitForWaveCompletion(lanePaths, agentRuns, timeoutMinutes, onProgress = null) {
@@ -2133,6 +2207,13 @@ function isClosureAgentId(agentId, lanePaths) {
2133
2207
  ].includes(agentId);
2134
2208
  }
2135
2209
 
2210
+ export function selectInitialWaveRuns(agentRuns, lanePaths) {
2211
+ const implementationRuns = (agentRuns || []).filter(
2212
+ (run) => !isClosureAgentId(run?.agent?.agentId, lanePaths),
2213
+ );
2214
+ return implementationRuns.length > 0 ? implementationRuns : agentRuns;
2215
+ }
2216
+
2136
2217
  function isLauncherSeedRequest(record) {
2137
2218
  return (
2138
2219
  record?.source === "launcher" &&
@@ -2191,7 +2272,7 @@ function buildFallbackExecutorState(executorState, executorId, attempt, reason)
2191
2272
  };
2192
2273
  }
2193
2274
 
2194
- function applyRetryFallbacks(agentRuns, failures, lanePaths, attemptNumber) {
2275
+ function applyRetryFallbacks(agentRuns, failures, lanePaths, attemptNumber, waveDefinition = null) {
2195
2276
  const failedAgentIds = new Set(failures.map((failure) => failure.agentId));
2196
2277
  let changed = false;
2197
2278
  const outcomes = new Map();
@@ -2257,6 +2338,7 @@ function applyRetryFallbacks(agentRuns, failures, lanePaths, attemptNumber) {
2257
2338
  continue;
2258
2339
  }
2259
2340
  run.agent.executorResolved = nextState;
2341
+ refreshResolvedSkillsForRun(run, waveDefinition, lanePaths);
2260
2342
  changed = true;
2261
2343
  outcomes.set(run.agent.agentId, {
2262
2344
  applied: true,
@@ -2478,7 +2560,7 @@ export function buildGateSnapshot({
2478
2560
  };
2479
2561
  }
2480
2562
 
2481
- export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths) {
2563
+ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths, waveDefinition = null) {
2482
2564
  const runsByAgentId = new Map(agentRuns.map((run) => [run.agent.agentId, run]));
2483
2565
  const pendingFeedback = (derivedState?.coordinationState?.humanFeedback || []).filter((record) =>
2484
2566
  isOpenCoordinationStatus(record.status),
@@ -2495,6 +2577,7 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
2495
2577
  failures,
2496
2578
  lanePaths,
2497
2579
  nextAttemptNumber,
2580
+ waveDefinition,
2498
2581
  );
2499
2582
  const retryBarrier = retryBarrierFromOutcomes(fallbackResolution.outcomes, failures);
2500
2583
  if (retryBarrier) {
@@ -2664,7 +2747,7 @@ function preflightWavesForExecutorAvailability(waves, lanePaths) {
2664
2747
  export async function runLauncherCli(argv) {
2665
2748
  const parsed = parseArgs(argv);
2666
2749
  if (parsed.help) {
2667
- printUsage(parsed.lanePaths);
2750
+ printUsage(parsed.lanePaths, parsed.options.terminalSurface);
2668
2751
  return;
2669
2752
  }
2670
2753
  const { lanePaths, options } = parsed;
@@ -2736,7 +2819,9 @@ export async function runLauncherCli(argv) {
2736
2819
  }
2737
2820
 
2738
2821
  try {
2739
- const staleArtifactCleanup = reconcileStaleLauncherArtifacts(lanePaths);
2822
+ const staleArtifactCleanup = reconcileStaleLauncherArtifacts(lanePaths, {
2823
+ terminalSurface: options.terminalSurface,
2824
+ });
2740
2825
  const context7BundleIndex = loadContext7BundleIndex(lanePaths.context7BundleIndexPath);
2741
2826
  const allWaves = parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile })
2742
2827
  .map((wave) =>
@@ -2813,6 +2898,9 @@ export async function runLauncherCli(argv) {
2813
2898
  ? reconciliation.state.completedWaves.join(", ")
2814
2899
  : "none";
2815
2900
  console.log(`[reconcile] added from status files: ${addedSummary}`);
2901
+ for (const blockedWave of reconciliation.blockedFromStatus || []) {
2902
+ console.log(formatReconcileBlockedWaveLine(blockedWave));
2903
+ }
2816
2904
  console.log(`[reconcile] completed waves now: ${completedSummary}`);
2817
2905
  return;
2818
2906
  }
@@ -2889,6 +2977,7 @@ export async function runLauncherCli(argv) {
2889
2977
  for (const runInfo of agentRuns) {
2890
2978
  await launchAgentSession(lanePaths, {
2891
2979
  wave: wave.wave,
2980
+ waveDefinition: wave,
2892
2981
  agent: runInfo.agent,
2893
2982
  sessionName: runInfo.sessionName,
2894
2983
  promptPath: runInfo.promptPath,
@@ -2918,6 +3007,9 @@ export async function runLauncherCli(argv) {
2918
3007
  }
2919
3008
 
2920
3009
  preflightWavesForExecutorAvailability(filteredWaves, lanePaths);
3010
+ const terminalRegistryEnabled = terminalSurfaceUsesTerminalRegistry(
3011
+ options.terminalSurface,
3012
+ );
2921
3013
 
2922
3014
  globalDashboard = buildGlobalDashboardState({
2923
3015
  lane: lanePaths.lane,
@@ -2929,7 +3021,7 @@ export async function runLauncherCli(argv) {
2929
3021
  });
2930
3022
  writeGlobalDashboard(lanePaths.globalDashboardPath, globalDashboard);
2931
3023
 
2932
- if (!options.keepTerminals) {
3024
+ if (terminalRegistryEnabled && !options.keepTerminals) {
2933
3025
  const removed = removeLaneTemporaryTerminalEntries(lanePaths.terminalsPath, lanePaths);
2934
3026
  if (removed > 0) {
2935
3027
  recordGlobalDashboardEvent(globalDashboard, {
@@ -2953,8 +3045,10 @@ export async function runLauncherCli(argv) {
2953
3045
  lanePaths,
2954
3046
  globalDashboard.runId || "global",
2955
3047
  );
2956
- appendTerminalEntries(lanePaths.terminalsPath, [globalDashboardTerminalEntry]);
2957
- globalDashboardTerminalAppended = true;
3048
+ if (terminalRegistryEnabled) {
3049
+ appendTerminalEntries(lanePaths.terminalsPath, [globalDashboardTerminalEntry]);
3050
+ globalDashboardTerminalAppended = true;
3051
+ }
2958
3052
  launchWaveDashboardSession(lanePaths, {
2959
3053
  sessionName: globalDashboardTerminalEntry.sessionName,
2960
3054
  dashboardPath: lanePaths.globalDashboardPath,
@@ -3033,8 +3127,10 @@ export async function runLauncherCli(argv) {
3033
3127
  runTag,
3034
3128
  options.dashboard,
3035
3129
  );
3036
- appendTerminalEntries(lanePaths.terminalsPath, terminalEntries);
3037
- terminalsAppended = true;
3130
+ if (terminalRegistryEnabled) {
3131
+ appendTerminalEntries(lanePaths.terminalsPath, terminalEntries);
3132
+ terminalsAppended = true;
3133
+ }
3038
3134
 
3039
3135
  const agentRuns = wave.agents.map((agent) => {
3040
3136
  const safeName = `wave-${wave.wave}-${agent.slug}`;
@@ -3149,7 +3245,10 @@ export async function runLauncherCli(argv) {
3149
3245
  });
3150
3246
  }
3151
3247
 
3152
- let runsToLaunch = agentRuns.filter((run) => !preCompletedAgentIds.has(run.agent.agentId));
3248
+ let runsToLaunch = selectInitialWaveRuns(
3249
+ agentRuns.filter((run) => !preCompletedAgentIds.has(run.agent.agentId)),
3250
+ lanePaths,
3251
+ );
3153
3252
  let attempt = 1;
3154
3253
  const feedbackStateByRequestId = new Map();
3155
3254
 
@@ -3219,6 +3318,7 @@ export async function runLauncherCli(argv) {
3219
3318
  flushDashboards();
3220
3319
  const launchResult = await launchAgentSession(lanePaths, {
3221
3320
  wave: wave.wave,
3321
+ waveDefinition: wave,
3222
3322
  agent: runInfo.agent,
3223
3323
  sessionName: runInfo.sessionName,
3224
3324
  promptPath: runInfo.promptPath,
@@ -3242,6 +3342,8 @@ export async function runLauncherCli(argv) {
3242
3342
  runInfo.lastPromptHash = launchResult?.promptHash || null;
3243
3343
  runInfo.lastContext7 = launchResult?.context7 || null;
3244
3344
  runInfo.lastExecutorId = launchResult?.executorId || runInfo.agent.executorResolved?.id || null;
3345
+ runInfo.lastSkillProjection =
3346
+ launchResult?.skills || summarizeResolvedSkills(runInfo.agent.skillsResolved);
3245
3347
  setWaveDashboardAgent(dashboardState, runInfo.agent.agentId, {
3246
3348
  state: "running",
3247
3349
  detail: "Session launched",
@@ -3271,16 +3373,19 @@ export async function runLauncherCli(argv) {
3271
3373
 
3272
3374
  const waitResult = await waitForWaveCompletion(
3273
3375
  lanePaths,
3274
- agentRuns,
3376
+ runsToLaunch,
3275
3377
  options.timeoutMinutes,
3276
3378
  ({ pendingAgentIds }) => {
3277
- refreshWaveDashboardAgentStates(dashboardState, agentRuns, pendingAgentIds, (event) =>
3278
- recordCombinedEvent(event),
3379
+ refreshWaveDashboardAgentStates(
3380
+ dashboardState,
3381
+ runsToLaunch,
3382
+ pendingAgentIds,
3383
+ (event) => recordCombinedEvent(event),
3279
3384
  );
3280
3385
  monitorWaveHumanFeedback({
3281
3386
  lanePaths,
3282
3387
  waveNumber: wave.wave,
3283
- agentRuns,
3388
+ agentRuns: runsToLaunch,
3284
3389
  orchestratorId: options.orchestratorId,
3285
3390
  coordinationLogPath: derivedState.coordinationLogPath,
3286
3391
  feedbackStateByRequestId,
@@ -3767,6 +3872,7 @@ export async function runLauncherCli(argv) {
3767
3872
  failures,
3768
3873
  derivedState,
3769
3874
  lanePaths,
3875
+ wave,
3770
3876
  );
3771
3877
  if (relaunchResolution.barrier) {
3772
3878
  for (const failure of relaunchResolution.barrier.failures) {