@chllming/wave-orchestration 0.6.2 → 0.7.0

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 (116) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/README.md +44 -8
  3. package/docs/agents/wave-orchestrator-role.md +50 -0
  4. package/docs/agents/wave-planner-role.md +39 -0
  5. package/docs/context7/bundles.json +9 -0
  6. package/docs/context7/planner-agent/README.md +25 -0
  7. package/docs/context7/planner-agent/manifest.json +83 -0
  8. package/docs/context7/planner-agent/papers/cooperbench-why-coding-agents-cannot-be-your-teammates-yet.md +3283 -0
  9. package/docs/context7/planner-agent/papers/dova-deliberation-first-multi-agent-orchestration-for-autonomous-research-automation.md +1699 -0
  10. package/docs/context7/planner-agent/papers/dpbench-large-language-models-struggle-with-simultaneous-coordination.md +2251 -0
  11. package/docs/context7/planner-agent/papers/incremental-planning-to-control-a-blackboard-based-problem-solver.md +1729 -0
  12. package/docs/context7/planner-agent/papers/silo-bench-a-scalable-environment-for-evaluating-distributed-coordination-in-multi-agent-llm-systems.md +3747 -0
  13. package/docs/context7/planner-agent/papers/todoevolve-learning-to-architect-agent-planning-systems.md +1675 -0
  14. package/docs/context7/planner-agent/papers/verified-multi-agent-orchestration-a-plan-execute-verify-replan-framework-for-complex-query-resolution.md +1173 -0
  15. package/docs/context7/planner-agent/papers/why-do-multi-agent-llm-systems-fail.md +5211 -0
  16. package/docs/context7/planner-agent/topics/planning-and-orchestration.md +24 -0
  17. package/docs/evals/README.md +96 -1
  18. package/docs/evals/arm-templates/README.md +13 -0
  19. package/docs/evals/arm-templates/full-wave.json +15 -0
  20. package/docs/evals/arm-templates/single-agent.json +15 -0
  21. package/docs/evals/benchmark-catalog.json +7 -0
  22. package/docs/evals/cases/README.md +47 -0
  23. package/docs/evals/cases/wave-blackboard-inbox-targeting.json +73 -0
  24. package/docs/evals/cases/wave-contradiction-conflict.json +104 -0
  25. package/docs/evals/cases/wave-expert-routing-preservation.json +69 -0
  26. package/docs/evals/cases/wave-hidden-profile-private-evidence.json +81 -0
  27. package/docs/evals/cases/wave-premature-closure-guard.json +71 -0
  28. package/docs/evals/cases/wave-silo-cross-agent-state.json +77 -0
  29. package/docs/evals/cases/wave-simultaneous-lockstep.json +92 -0
  30. package/docs/evals/cooperbench/real-world-mitigation.md +341 -0
  31. package/docs/evals/external-benchmarks.json +85 -0
  32. package/docs/evals/external-command-config.sample.json +9 -0
  33. package/docs/evals/external-command-config.swe-bench-pro.json +8 -0
  34. package/docs/evals/pilots/README.md +47 -0
  35. package/docs/evals/pilots/swe-bench-pro-public-full-wave-review-10.json +64 -0
  36. package/docs/evals/pilots/swe-bench-pro-public-pilot.json +111 -0
  37. package/docs/evals/wave-benchmark-program.md +302 -0
  38. package/docs/guides/planner.md +48 -11
  39. package/docs/plans/context7-wave-orchestrator.md +20 -0
  40. package/docs/plans/current-state.md +9 -1
  41. package/docs/plans/examples/wave-benchmark-improvement.md +108 -0
  42. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  43. package/docs/plans/examples/wave-example-rollout-fidelity.md +340 -0
  44. package/docs/plans/wave-orchestrator.md +73 -11
  45. package/docs/plans/waves/reviews/wave-1-benchmark-operator.md +118 -0
  46. package/docs/reference/coordination-and-closure.md +436 -0
  47. package/docs/reference/live-proof-waves.md +25 -3
  48. package/docs/reference/npmjs-trusted-publishing.md +3 -3
  49. package/docs/reference/proof-metrics.md +90 -0
  50. package/docs/reference/runtime-config/README.md +61 -0
  51. package/docs/reference/sample-waves.md +29 -18
  52. package/docs/reference/wave-control.md +164 -0
  53. package/docs/reference/wave-planning-lessons.md +131 -0
  54. package/package.json +5 -4
  55. package/releases/manifest.json +33 -0
  56. package/scripts/research/agent-context-archive.mjs +18 -0
  57. package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +17 -0
  58. package/scripts/research/sync-planner-context7-bundle.mjs +133 -0
  59. package/scripts/wave-autonomous.mjs +2 -4
  60. package/scripts/wave-orchestrator/adhoc.mjs +32 -11
  61. package/scripts/wave-orchestrator/artifact-schemas.mjs +232 -0
  62. package/scripts/wave-orchestrator/autonomous.mjs +27 -6
  63. package/scripts/wave-orchestrator/benchmark-cases.mjs +374 -0
  64. package/scripts/wave-orchestrator/benchmark-external.mjs +1384 -0
  65. package/scripts/wave-orchestrator/benchmark.mjs +972 -0
  66. package/scripts/wave-orchestrator/clarification-triage.mjs +78 -12
  67. package/scripts/wave-orchestrator/config.mjs +175 -0
  68. package/scripts/wave-orchestrator/control-cli.mjs +1123 -0
  69. package/scripts/wave-orchestrator/control-plane.mjs +697 -0
  70. package/scripts/wave-orchestrator/coord-cli.mjs +360 -2
  71. package/scripts/wave-orchestrator/coordination-store.mjs +211 -9
  72. package/scripts/wave-orchestrator/coordination.mjs +84 -0
  73. package/scripts/wave-orchestrator/dashboard-renderer.mjs +38 -3
  74. package/scripts/wave-orchestrator/dashboard-state.mjs +22 -0
  75. package/scripts/wave-orchestrator/evals.mjs +23 -0
  76. package/scripts/wave-orchestrator/executors.mjs +3 -2
  77. package/scripts/wave-orchestrator/feedback.mjs +55 -0
  78. package/scripts/wave-orchestrator/install.mjs +253 -26
  79. package/scripts/wave-orchestrator/launcher-closure.mjs +4 -1
  80. package/scripts/wave-orchestrator/launcher-runtime.mjs +24 -21
  81. package/scripts/wave-orchestrator/launcher.mjs +800 -35
  82. package/scripts/wave-orchestrator/package-update-notice.mjs +230 -0
  83. package/scripts/wave-orchestrator/package-version.mjs +32 -0
  84. package/scripts/wave-orchestrator/planner-context.mjs +75 -0
  85. package/scripts/wave-orchestrator/planner.mjs +2270 -136
  86. package/scripts/wave-orchestrator/proof-cli.mjs +195 -0
  87. package/scripts/wave-orchestrator/proof-registry.mjs +317 -0
  88. package/scripts/wave-orchestrator/replay.mjs +10 -4
  89. package/scripts/wave-orchestrator/retry-cli.mjs +184 -0
  90. package/scripts/wave-orchestrator/retry-control.mjs +225 -0
  91. package/scripts/wave-orchestrator/shared.mjs +26 -0
  92. package/scripts/wave-orchestrator/swe-bench-pro-task.mjs +1004 -0
  93. package/scripts/wave-orchestrator/traces.mjs +157 -2
  94. package/scripts/wave-orchestrator/wave-control-client.mjs +532 -0
  95. package/scripts/wave-orchestrator/wave-control-schema.mjs +309 -0
  96. package/scripts/wave-orchestrator/wave-files.mjs +17 -5
  97. package/scripts/wave.mjs +39 -2
  98. package/skills/repo-coding-rules/SKILL.md +1 -0
  99. package/skills/role-cont-eval/SKILL.md +1 -0
  100. package/skills/role-cont-qa/SKILL.md +13 -6
  101. package/skills/role-deploy/SKILL.md +1 -0
  102. package/skills/role-documentation/SKILL.md +4 -0
  103. package/skills/role-implementation/SKILL.md +4 -0
  104. package/skills/role-infra/SKILL.md +2 -1
  105. package/skills/role-integration/SKILL.md +15 -8
  106. package/skills/role-planner/SKILL.md +39 -0
  107. package/skills/role-planner/skill.json +21 -0
  108. package/skills/role-research/SKILL.md +1 -0
  109. package/skills/role-security/SKILL.md +2 -2
  110. package/skills/runtime-claude/SKILL.md +2 -1
  111. package/skills/runtime-codex/SKILL.md +1 -0
  112. package/skills/runtime-local/SKILL.md +2 -0
  113. package/skills/runtime-opencode/SKILL.md +1 -0
  114. package/skills/wave-core/SKILL.md +25 -6
  115. package/skills/wave-core/references/marker-syntax.md +16 -8
  116. package/wave.config.json +45 -0
@@ -8,7 +8,11 @@ import {
8
8
  readMaterializedCoordinationState,
9
9
  } from "./coordination-store.mjs";
10
10
  import { createFeedbackRequest } from "./feedback.mjs";
11
- import { ensureDirectory, writeTextAtomic } from "./shared.mjs";
11
+ import {
12
+ DEFAULT_COORDINATION_ACK_TIMEOUT_MS,
13
+ ensureDirectory,
14
+ writeTextAtomic,
15
+ } from "./shared.mjs";
12
16
 
13
17
  const MAX_ROUTED_CLARIFICATION_CYCLES = 2;
14
18
 
@@ -303,12 +307,12 @@ function openEscalationForClarification(coordinationState, clarificationId) {
303
307
  );
304
308
  }
305
309
 
306
- function latestRouteAttempt(requests) {
307
- return requests.reduce(
308
- (maxAttempt, record) =>
309
- Math.max(maxAttempt, Number.parseInt(String(record.attempt || 0), 10) || 0),
310
- 0,
311
- );
310
+ function recordAgeMs(record, nowMs = Date.now()) {
311
+ const startedAtMs = Date.parse(record?.createdAt || record?.updatedAt || "");
312
+ if (!Number.isFinite(startedAtMs)) {
313
+ return null;
314
+ }
315
+ return Math.max(0, nowMs - startedAtMs);
312
316
  }
313
317
 
314
318
  function supersedeOpenRequests(coordinationLogPath, requests, attempt) {
@@ -326,6 +330,23 @@ function supersedeOpenRequests(coordinationLogPath, requests, attempt) {
326
330
  }
327
331
  }
328
332
 
333
+ function supersedeOpenEscalations(triagePath, coordinationLogPath, escalations, attempt, detail) {
334
+ for (const escalation of escalations) {
335
+ if (!isOpenCoordinationStatus(escalation.status)) {
336
+ continue;
337
+ }
338
+ const nextRecord = {
339
+ ...escalation,
340
+ status: "superseded",
341
+ detail,
342
+ attempt,
343
+ updatedAt: undefined,
344
+ };
345
+ appendTriageRecord(triagePath, nextRecord);
346
+ appendCoordinationRecord(coordinationLogPath, nextRecord);
347
+ }
348
+ }
349
+
329
350
  function createClarificationRoute({
330
351
  triagePath,
331
352
  coordinationLogPath,
@@ -409,6 +430,7 @@ function escalateClarificationToHuman({
409
430
  orchestratorId,
410
431
  question: record.summary || "Clarification requested",
411
432
  context: record.detail || "",
433
+ recordTelemetry: true,
412
434
  });
413
435
  const escalationId = `escalation-${humanRequest.requestId}`;
414
436
  const escalationRecord = {
@@ -440,6 +462,7 @@ export function triageClarificationRequests({
440
462
  orchestratorId,
441
463
  attempt = 0,
442
464
  resolutionContext = {},
465
+ ackTimeoutMs = DEFAULT_COORDINATION_ACK_TIMEOUT_MS,
443
466
  }) {
444
467
  ensureDirectory(lanePaths.feedbackTriageDir);
445
468
  const triagePath = triageLogPath(lanePaths, wave.wave);
@@ -453,11 +476,29 @@ export function triageClarificationRequests({
453
476
  const openLinkedRequests = linkedRequests.filter((entry) =>
454
477
  isOpenCoordinationStatus(entry.status),
455
478
  );
479
+ const openAckPendingLinkedRequests = openLinkedRequests.filter(
480
+ (entry) => entry.status === "open",
481
+ );
482
+ const activeLinkedRequests = openLinkedRequests.filter((entry) => entry.status !== "open");
456
483
  const resolvedLinkedRequest = linkedRequests.find((entry) =>
457
484
  ["resolved", "closed"].includes(entry.status),
458
485
  );
459
486
  const resolvedEscalation = resolvedEscalationForClarification(coordinationState, record.id);
487
+ const openEscalations = (coordinationState?.humanEscalations || []).filter(
488
+ (entry) =>
489
+ entry.closureCondition === clarificationClosureCondition(record.id) &&
490
+ isOpenCoordinationStatus(entry.status),
491
+ );
460
492
  if (resolvedLinkedRequest || resolvedEscalation) {
493
+ if (openEscalations.length > 0) {
494
+ supersedeOpenEscalations(
495
+ triagePath,
496
+ coordinationLogPath,
497
+ openEscalations,
498
+ attempt,
499
+ `Superseded because clarification ${record.id} was already resolved.`,
500
+ );
501
+ }
461
502
  updateClarificationRecord(
462
503
  coordinationLogPath,
463
504
  record,
@@ -476,6 +517,15 @@ export function triageClarificationRequests({
476
517
  coordinationState,
477
518
  });
478
519
  if (resolution?.type === "policy") {
520
+ if (openEscalations.length > 0) {
521
+ supersedeOpenEscalations(
522
+ triagePath,
523
+ coordinationLogPath,
524
+ openEscalations,
525
+ attempt,
526
+ `Superseded by policy resolution for clarification ${record.id}.`,
527
+ );
528
+ }
479
529
  updateClarificationRecord(coordinationLogPath, record, "resolved", resolution.guidance, attempt);
480
530
  appendTriageRecord(triagePath, {
481
531
  id: `triage-${record.id}-policy`,
@@ -512,8 +562,17 @@ export function triageClarificationRequests({
512
562
  }
513
563
 
514
564
  if (resolution?.type === "route") {
565
+ if (openEscalations.length > 0) {
566
+ supersedeOpenEscalations(
567
+ triagePath,
568
+ coordinationLogPath,
569
+ openEscalations,
570
+ attempt,
571
+ `Superseded by routed clarification follow-up for ${record.id}.`,
572
+ );
573
+ changed = true;
574
+ }
515
575
  const routeCycles = linkedRequests.length;
516
- const lastRouteAttempt = latestRouteAttempt(linkedRequests);
517
576
  if (openLinkedRequests.length === 0) {
518
577
  createClarificationRoute({
519
578
  triagePath,
@@ -529,8 +588,15 @@ export function triageClarificationRequests({
529
588
  changed = true;
530
589
  continue;
531
590
  }
532
- if (attempt > lastRouteAttempt && routeCycles < MAX_ROUTED_CLARIFICATION_CYCLES) {
533
- supersedeOpenRequests(coordinationLogPath, openLinkedRequests, attempt);
591
+ if (activeLinkedRequests.length > 0) {
592
+ continue;
593
+ }
594
+ const timedOutLinkedRequests = openAckPendingLinkedRequests.filter((entry) => {
595
+ const ageMs = recordAgeMs(entry);
596
+ return Number.isFinite(ageMs) && ageMs >= ackTimeoutMs;
597
+ });
598
+ if (timedOutLinkedRequests.length > 0 && routeCycles < MAX_ROUTED_CLARIFICATION_CYCLES) {
599
+ supersedeOpenRequests(coordinationLogPath, timedOutLinkedRequests, attempt);
534
600
  createClarificationRoute({
535
601
  triagePath,
536
602
  coordinationLogPath,
@@ -545,8 +611,8 @@ export function triageClarificationRequests({
545
611
  changed = true;
546
612
  continue;
547
613
  }
548
- if (attempt > lastRouteAttempt && routeCycles >= MAX_ROUTED_CLARIFICATION_CYCLES) {
549
- if (openEscalationForClarification(coordinationState, record.id)) {
614
+ if (timedOutLinkedRequests.length > 0 && routeCycles >= MAX_ROUTED_CLARIFICATION_CYCLES) {
615
+ if (openEscalations.length > 0 || openEscalationForClarification(coordinationState, record.id)) {
550
616
  continue;
551
617
  }
552
618
  const escalationRecord = escalateClarificationToHuman({
@@ -1,11 +1,17 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { WORKSPACE_ROOT } from "./roots.mjs";
4
+ import {
5
+ PLANNER_CONTEXT7_BUNDLE_ID,
6
+ PLANNER_CONTEXT7_DEFAULT_QUERY,
7
+ PLANNER_CONTEXT7_RESEARCH_TOPIC_PATHS,
8
+ } from "./planner-context.mjs";
4
9
  import {
5
10
  emptySkillsConfig,
6
11
  mergeSkillsConfig,
7
12
  normalizeSkillsConfig,
8
13
  } from "./skills.mjs";
14
+ import { normalizeWaveControlReportMode } from "./wave-control-schema.mjs";
9
15
 
10
16
  const REPO_ROOT = WORKSPACE_ROOT;
11
17
 
@@ -34,6 +40,30 @@ export const DEFAULT_REQUIRED_PROMPT_REFERENCES = [
34
40
  "docs/reference/repository-guidance.md",
35
41
  "docs/research/agent-context-sources.md",
36
42
  ];
43
+ export const DEFAULT_PLANNER_AGENTIC_EXECUTOR_PROFILE = "planning-readonly";
44
+ export const DEFAULT_PLANNER_AGENTIC_MAX_WAVES = 3;
45
+ export const DEFAULT_PLANNER_AGENTIC_MAX_REPLAN_ITERATIONS = 1;
46
+ export const DEFAULT_PLANNER_AGENTIC_CONTEXT7_BUNDLE = PLANNER_CONTEXT7_BUNDLE_ID;
47
+ export const DEFAULT_PLANNER_AGENTIC_CONTEXT7_QUERY = PLANNER_CONTEXT7_DEFAULT_QUERY;
48
+ export const DEFAULT_PLANNER_AGENTIC_CORE_CONTEXT_PATHS = [
49
+ "AGENTS.md",
50
+ "wave.config.json",
51
+ "docs/roadmap.md",
52
+ "docs/plans/current-state.md",
53
+ "docs/plans/master-plan.md",
54
+ "docs/plans/wave-orchestrator.md",
55
+ "docs/reference/sample-waves.md",
56
+ "docs/plans/examples/wave-example-live-proof.md",
57
+ "docs/reference/live-proof-waves.md",
58
+ "docs/plans/component-cutover-matrix.md",
59
+ "docs/plans/component-cutover-matrix.json",
60
+ "docs/reference/wave-planning-lessons.md",
61
+ "docs/research/coordination-failure-review.md",
62
+ ];
63
+ export const DEFAULT_PLANNER_AGENTIC_LESSONS_PATHS = [
64
+ "docs/reference/wave-planning-lessons.md",
65
+ ];
66
+ export const DEFAULT_PLANNER_AGENTIC_RESEARCH_TOPIC_PATHS = PLANNER_CONTEXT7_RESEARCH_TOPIC_PATHS;
37
67
  export const SUPPORTED_EXECUTOR_MODES = ["codex", "claude", "opencode", "local"];
38
68
  export const DEFAULT_EXECUTOR_MODE = "codex";
39
69
  export const DEFAULT_CODEX_COMMAND = "codex";
@@ -41,6 +71,26 @@ export const DEFAULT_CODEX_SANDBOX_MODE = "danger-full-access";
41
71
  export const CODEX_SANDBOX_MODES = ["read-only", "workspace-write", "danger-full-access"];
42
72
  export const DEFAULT_CLAUDE_COMMAND = "claude";
43
73
  export const DEFAULT_OPENCODE_COMMAND = "opencode";
74
+ export const DEFAULT_WAVE_CONTROL_AUTH_TOKEN_ENV_VAR = "WAVE_CONTROL_AUTH_TOKEN";
75
+ export const DEFAULT_WAVE_CONTROL_REPORT_MODE = "metadata-plus-selected";
76
+ export const DEFAULT_WAVE_CONTROL_REQUEST_TIMEOUT_MS = 5000;
77
+ export const DEFAULT_WAVE_CONTROL_FLUSH_BATCH_SIZE = 25;
78
+ export const DEFAULT_WAVE_CONTROL_MAX_PENDING_EVENTS = 1000;
79
+ export const DEFAULT_WAVE_CONTROL_SELECTED_ARTIFACT_KINDS = [
80
+ "trace-run-metadata",
81
+ "trace-quality",
82
+ "trace-outcome",
83
+ "integration-summary",
84
+ "proof-registry",
85
+ "agent-summary",
86
+ "control-plane-log",
87
+ "benchmark-results",
88
+ "benchmark-failure-review",
89
+ "verification-stdout",
90
+ "verification-stderr",
91
+ "verification-output-manifest",
92
+ "benchmark-patch-manifest",
93
+ ];
44
94
  const LEGACY_EVALUATOR_ROLE_KEYS = new Map([
45
95
  ["evaluatorAgentId", "contQaAgentId"],
46
96
  ["evaluatorRolePromptPath", "contQaRolePromptPath"],
@@ -334,6 +384,65 @@ function normalizeValidation(rawValidation = {}) {
334
384
  };
335
385
  }
336
386
 
387
+ function normalizePlannerAgentic(rawAgentic = {}) {
388
+ const plannerAgentic =
389
+ rawAgentic && typeof rawAgentic === "object" && !Array.isArray(rawAgentic)
390
+ ? rawAgentic
391
+ : {};
392
+ return {
393
+ executorProfile: String(
394
+ plannerAgentic.executorProfile || DEFAULT_PLANNER_AGENTIC_EXECUTOR_PROFILE,
395
+ )
396
+ .trim()
397
+ .toLowerCase(),
398
+ defaultMaxWaves:
399
+ normalizeOptionalPositiveInt(
400
+ plannerAgentic.defaultMaxWaves,
401
+ "planner.agentic.defaultMaxWaves",
402
+ DEFAULT_PLANNER_AGENTIC_MAX_WAVES,
403
+ ) || DEFAULT_PLANNER_AGENTIC_MAX_WAVES,
404
+ maxReplanIterations:
405
+ normalizeOptionalPositiveInt(
406
+ plannerAgentic.maxReplanIterations,
407
+ "planner.agentic.maxReplanIterations",
408
+ DEFAULT_PLANNER_AGENTIC_MAX_REPLAN_ITERATIONS,
409
+ ) || DEFAULT_PLANNER_AGENTIC_MAX_REPLAN_ITERATIONS,
410
+ context7Bundle: String(
411
+ plannerAgentic.context7Bundle || DEFAULT_PLANNER_AGENTIC_CONTEXT7_BUNDLE,
412
+ )
413
+ .trim()
414
+ .toLowerCase(),
415
+ context7Query:
416
+ normalizeOptionalString(
417
+ plannerAgentic.context7Query,
418
+ DEFAULT_PLANNER_AGENTIC_CONTEXT7_QUERY,
419
+ ) || DEFAULT_PLANNER_AGENTIC_CONTEXT7_QUERY,
420
+ coreContextPaths:
421
+ normalizeOptionalPathArray(
422
+ plannerAgentic.coreContextPaths,
423
+ "planner.agentic.coreContextPaths",
424
+ ) || DEFAULT_PLANNER_AGENTIC_CORE_CONTEXT_PATHS,
425
+ lessonsPaths:
426
+ normalizeOptionalPathArray(
427
+ plannerAgentic.lessonsPaths,
428
+ "planner.agentic.lessonsPaths",
429
+ ) || DEFAULT_PLANNER_AGENTIC_LESSONS_PATHS,
430
+ researchTopicPaths:
431
+ normalizeOptionalPathArray(
432
+ plannerAgentic.researchTopicPaths,
433
+ "planner.agentic.researchTopicPaths",
434
+ ) || DEFAULT_PLANNER_AGENTIC_RESEARCH_TOPIC_PATHS,
435
+ };
436
+ }
437
+
438
+ function normalizePlanner(rawPlanner = {}) {
439
+ const planner =
440
+ rawPlanner && typeof rawPlanner === "object" && !Array.isArray(rawPlanner) ? rawPlanner : {};
441
+ return {
442
+ agentic: normalizePlannerAgentic(planner.agentic),
443
+ };
444
+ }
445
+
337
446
  function normalizeCapabilityRouting(rawCapabilityRouting = {}) {
338
447
  const preferredAgentsInput =
339
448
  rawCapabilityRouting && typeof rawCapabilityRouting === "object"
@@ -421,6 +530,62 @@ function normalizeRuntimePolicy(rawRuntimePolicy = {}) {
421
530
  };
422
531
  }
423
532
 
533
+ function normalizeWaveControl(rawWaveControl = {}, label = "waveControl") {
534
+ const waveControl =
535
+ rawWaveControl && typeof rawWaveControl === "object" && !Array.isArray(rawWaveControl)
536
+ ? rawWaveControl
537
+ : {};
538
+ const reportMode = normalizeWaveControlReportMode(
539
+ waveControl.reportMode,
540
+ `${label}.reportMode`,
541
+ DEFAULT_WAVE_CONTROL_REPORT_MODE,
542
+ );
543
+ const enabled =
544
+ reportMode !== "disabled" && normalizeOptionalBoolean(waveControl.enabled, true);
545
+ return {
546
+ enabled,
547
+ endpoint: normalizeOptionalString(waveControl.endpoint, null),
548
+ workspaceId: normalizeOptionalString(waveControl.workspaceId, null),
549
+ projectId: normalizeOptionalString(waveControl.projectId, null),
550
+ authTokenEnvVar:
551
+ normalizeOptionalString(waveControl.authTokenEnvVar, DEFAULT_WAVE_CONTROL_AUTH_TOKEN_ENV_VAR) ||
552
+ DEFAULT_WAVE_CONTROL_AUTH_TOKEN_ENV_VAR,
553
+ reportMode,
554
+ uploadArtifactKinds: normalizeOptionalStringArray(
555
+ waveControl.uploadArtifactKinds,
556
+ DEFAULT_WAVE_CONTROL_SELECTED_ARTIFACT_KINDS,
557
+ ),
558
+ requestTimeoutMs:
559
+ normalizeOptionalPositiveInt(
560
+ waveControl.requestTimeoutMs,
561
+ `${label}.requestTimeoutMs`,
562
+ DEFAULT_WAVE_CONTROL_REQUEST_TIMEOUT_MS,
563
+ ) || DEFAULT_WAVE_CONTROL_REQUEST_TIMEOUT_MS,
564
+ flushBatchSize:
565
+ normalizeOptionalPositiveInt(
566
+ waveControl.flushBatchSize,
567
+ `${label}.flushBatchSize`,
568
+ DEFAULT_WAVE_CONTROL_FLUSH_BATCH_SIZE,
569
+ ) || DEFAULT_WAVE_CONTROL_FLUSH_BATCH_SIZE,
570
+ maxPendingEvents:
571
+ normalizeOptionalPositiveInt(
572
+ waveControl.maxPendingEvents,
573
+ `${label}.maxPendingEvents`,
574
+ DEFAULT_WAVE_CONTROL_MAX_PENDING_EVENTS,
575
+ ) || DEFAULT_WAVE_CONTROL_MAX_PENDING_EVENTS,
576
+ captureCoordinationRecords: normalizeOptionalBoolean(
577
+ waveControl.captureCoordinationRecords,
578
+ true,
579
+ ),
580
+ captureControlPlaneEvents: normalizeOptionalBoolean(
581
+ waveControl.captureControlPlaneEvents,
582
+ true,
583
+ ),
584
+ captureTraceBundles: normalizeOptionalBoolean(waveControl.captureTraceBundles, true),
585
+ captureBenchmarkRuns: normalizeOptionalBoolean(waveControl.captureBenchmarkRuns, true),
586
+ };
587
+ }
588
+
424
589
  function normalizeLaneSkills(rawSkills = {}, lane = "skills", options = {}) {
425
590
  return normalizeSkillsConfig(rawSkills, lane, options);
426
591
  }
@@ -811,9 +976,11 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
811
976
  roles: normalizeRoles(rawConfig.roles),
812
977
  validation: normalizeValidation(rawConfig.validation),
813
978
  executors: normalizeExecutors(rawConfig.executors),
979
+ planner: normalizePlanner(rawConfig.planner),
814
980
  skills: normalizeLaneSkills(rawConfig.skills, "skills"),
815
981
  capabilityRouting: normalizeCapabilityRouting(rawConfig.capabilityRouting),
816
982
  runtimePolicy: normalizeRuntimePolicy(rawConfig.runtimePolicy),
983
+ waveControl: normalizeWaveControl(rawConfig.waveControl, "waveControl"),
817
984
  sharedPlanDocs,
818
985
  lanes,
819
986
  configPath,
@@ -867,6 +1034,13 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
867
1034
  ? { fallbackExecutorOrder: laneConfig.fallbackExecutorOrder }
868
1035
  : {}),
869
1036
  });
1037
+ const waveControl = normalizeWaveControl(
1038
+ {
1039
+ ...config.waveControl,
1040
+ ...(laneConfig.waveControl || {}),
1041
+ },
1042
+ `${lane}.waveControl`,
1043
+ );
870
1044
  return {
871
1045
  lane,
872
1046
  docsDir,
@@ -882,6 +1056,7 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
882
1056
  skills,
883
1057
  capabilityRouting,
884
1058
  runtimePolicy,
1059
+ waveControl,
885
1060
  paths: {
886
1061
  terminalsPath: normalizeRepoRelativePath(
887
1062
  laneConfig.terminalsPath || config.paths.terminalsPath,