@chllming/wave-orchestration 0.9.0 → 0.9.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +133 -20
  4. package/docs/README.md +12 -4
  5. package/docs/agents/wave-security-role.md +1 -0
  6. package/docs/architecture/README.md +1498 -0
  7. package/docs/concepts/operating-modes.md +2 -2
  8. package/docs/guides/author-and-run-waves.md +14 -4
  9. package/docs/guides/planner.md +2 -2
  10. package/docs/guides/{recommendations-0.9.0.md → recommendations-0.9.2.md} +8 -7
  11. package/docs/guides/sandboxed-environments.md +158 -0
  12. package/docs/guides/terminal-surfaces.md +14 -12
  13. package/docs/plans/current-state.md +11 -3
  14. package/docs/plans/end-state-architecture.md +3 -1
  15. package/docs/plans/examples/wave-example-design-handoff.md +1 -1
  16. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  17. package/docs/plans/migration.md +70 -19
  18. package/docs/plans/sandbox-end-state-architecture.md +153 -0
  19. package/docs/reference/cli-reference.md +71 -7
  20. package/docs/reference/coordination-and-closure.md +18 -1
  21. package/docs/reference/corridor.md +225 -0
  22. package/docs/reference/github-packages-setup.md +1 -1
  23. package/docs/reference/migration-0.2-to-0.5.md +9 -7
  24. package/docs/reference/npmjs-token-publishing.md +53 -0
  25. package/docs/reference/npmjs-trusted-publishing.md +4 -50
  26. package/docs/reference/package-publishing-flow.md +272 -0
  27. package/docs/reference/runtime-config/README.md +61 -3
  28. package/docs/reference/sample-waves.md +5 -5
  29. package/docs/reference/skills.md +1 -1
  30. package/docs/reference/wave-control.md +358 -27
  31. package/docs/roadmap.md +39 -204
  32. package/package.json +1 -1
  33. package/releases/manifest.json +38 -0
  34. package/scripts/wave-cli-bootstrap.mjs +52 -1
  35. package/scripts/wave-orchestrator/agent-process-runner.mjs +344 -0
  36. package/scripts/wave-orchestrator/agent-state.mjs +0 -1
  37. package/scripts/wave-orchestrator/artifact-schemas.mjs +7 -0
  38. package/scripts/wave-orchestrator/autonomous.mjs +47 -14
  39. package/scripts/wave-orchestrator/closure-engine.mjs +138 -17
  40. package/scripts/wave-orchestrator/config.mjs +199 -3
  41. package/scripts/wave-orchestrator/context7.mjs +231 -29
  42. package/scripts/wave-orchestrator/control-cli.mjs +42 -5
  43. package/scripts/wave-orchestrator/coordination.mjs +14 -0
  44. package/scripts/wave-orchestrator/corridor.mjs +363 -0
  45. package/scripts/wave-orchestrator/dashboard-renderer.mjs +115 -43
  46. package/scripts/wave-orchestrator/derived-state-engine.mjs +44 -4
  47. package/scripts/wave-orchestrator/gate-engine.mjs +126 -38
  48. package/scripts/wave-orchestrator/install.mjs +46 -0
  49. package/scripts/wave-orchestrator/launcher-progress.mjs +91 -0
  50. package/scripts/wave-orchestrator/launcher-runtime.mjs +290 -75
  51. package/scripts/wave-orchestrator/launcher.mjs +201 -53
  52. package/scripts/wave-orchestrator/ledger.mjs +7 -2
  53. package/scripts/wave-orchestrator/planner.mjs +1 -0
  54. package/scripts/wave-orchestrator/projection-writer.mjs +36 -1
  55. package/scripts/wave-orchestrator/provider-runtime.mjs +104 -0
  56. package/scripts/wave-orchestrator/reducer-snapshot.mjs +6 -0
  57. package/scripts/wave-orchestrator/retry-control.mjs +3 -3
  58. package/scripts/wave-orchestrator/retry-engine.mjs +93 -6
  59. package/scripts/wave-orchestrator/role-helpers.mjs +30 -0
  60. package/scripts/wave-orchestrator/session-supervisor.mjs +94 -85
  61. package/scripts/wave-orchestrator/shared.mjs +1 -0
  62. package/scripts/wave-orchestrator/supervisor-cli.mjs +1306 -0
  63. package/scripts/wave-orchestrator/terminals.mjs +12 -32
  64. package/scripts/wave-orchestrator/tmux-adapter.mjs +300 -0
  65. package/scripts/wave-orchestrator/traces.mjs +25 -0
  66. package/scripts/wave-orchestrator/wave-control-client.mjs +14 -1
  67. package/scripts/wave-orchestrator/wave-files.mjs +38 -5
  68. package/scripts/wave.mjs +13 -0
@@ -9,6 +9,15 @@ import {
9
9
  sleep,
10
10
  writeJsonAtomic,
11
11
  } from "./shared.mjs";
12
+ import {
13
+ DEFAULT_CONTEXT7_API_KEY_ENV_VAR,
14
+ DEFAULT_WAVE_CONTROL_ENDPOINT,
15
+ } from "./config.mjs";
16
+ import {
17
+ isDefaultWaveControlEndpoint,
18
+ readJsonResponse,
19
+ resolveWaveControlAuthToken,
20
+ } from "./provider-runtime.mjs";
12
21
 
13
22
  export const DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH = path.join(
14
23
  REPO_ROOT,
@@ -277,16 +286,10 @@ function renderPrefetchedContextText({ selection, results, budget }) {
277
286
  return trimContextText(sections.join("\n\n"), budget);
278
287
  }
279
288
 
280
- async function requestContext7(fetchImpl, url, { apiKey, expectText = false, maxRetries = 3 } = {}) {
289
+ async function requestContext7(fetchImpl, request, { expectText = false, maxRetries = 3 } = {}) {
281
290
  let lastError = null;
282
291
  for (let attempt = 0; attempt < maxRetries; attempt += 1) {
283
- const response = await fetchImpl(url, {
284
- method: "GET",
285
- headers: {
286
- Authorization: `Bearer ${apiKey}`,
287
- Accept: expectText ? "text/plain, application/json" : "application/json",
288
- },
289
- });
292
+ const response = await request();
290
293
  if (response.ok) {
291
294
  return expectText ? response.text() : response.json();
292
295
  }
@@ -296,7 +299,7 @@ async function requestContext7(fetchImpl, url, { apiKey, expectText = false, max
296
299
  : 0;
297
300
  let payload = null;
298
301
  try {
299
- payload = await response.json();
302
+ payload = await readJsonResponse(response, null);
300
303
  } catch {
301
304
  payload = null;
302
305
  }
@@ -311,7 +314,193 @@ async function requestContext7(fetchImpl, url, { apiKey, expectText = false, max
311
314
  throw lastError || new Error("Context7 request failed.");
312
315
  }
313
316
 
314
- async function resolveLibraryId(fetchImpl, library, selection, apiKey) {
317
+ function buildDirectContext7Requester(fetchImpl, apiKey) {
318
+ return {
319
+ async search(params) {
320
+ const url = `${CONTEXT7_SEARCH_URL}?${params.toString()}`;
321
+ return requestContext7(
322
+ fetchImpl,
323
+ () =>
324
+ fetchImpl(url, {
325
+ method: "GET",
326
+ headers: {
327
+ Authorization: `Bearer ${apiKey}`,
328
+ Accept: "application/json",
329
+ },
330
+ }),
331
+ );
332
+ },
333
+ async context(params) {
334
+ const url = `${CONTEXT7_CONTEXT_URL}?${params.toString()}`;
335
+ return requestContext7(
336
+ fetchImpl,
337
+ () =>
338
+ fetchImpl(url, {
339
+ method: "GET",
340
+ headers: {
341
+ Authorization: `Bearer ${apiKey}`,
342
+ Accept: "text/plain, application/json",
343
+ },
344
+ }),
345
+ { expectText: true },
346
+ );
347
+ },
348
+ };
349
+ }
350
+
351
+ function buildBrokerContext7Requester(fetchImpl, lanePaths) {
352
+ const waveControl = lanePaths?.waveControl || lanePaths?.laneProfile?.waveControl || {};
353
+ const endpoint = String(waveControl.endpoint || DEFAULT_WAVE_CONTROL_ENDPOINT).trim();
354
+ if (!endpoint || isDefaultWaveControlEndpoint(endpoint)) {
355
+ throw new Error("Context7 broker mode requires an owned Wave Control endpoint.");
356
+ }
357
+ const authToken = resolveWaveControlAuthToken(waveControl);
358
+ if (!authToken) {
359
+ throw new Error("WAVE_API_TOKEN is not set; skipping Context7 broker prefetch.");
360
+ }
361
+ const baseEndpoint = endpoint.replace(/\/$/, "");
362
+ return {
363
+ async search(params) {
364
+ return requestContext7(
365
+ fetchImpl,
366
+ () =>
367
+ fetchImpl(`${baseEndpoint}/providers/context7/search?${params.toString()}`, {
368
+ method: "GET",
369
+ headers: {
370
+ authorization: `Bearer ${authToken}`,
371
+ accept: "application/json",
372
+ },
373
+ }),
374
+ );
375
+ },
376
+ async context(params) {
377
+ return requestContext7(
378
+ fetchImpl,
379
+ () =>
380
+ fetchImpl(`${baseEndpoint}/providers/context7/context?${params.toString()}`, {
381
+ method: "GET",
382
+ headers: {
383
+ authorization: `Bearer ${authToken}`,
384
+ accept: "text/plain, application/json",
385
+ },
386
+ }),
387
+ { expectText: true },
388
+ );
389
+ },
390
+ };
391
+ }
392
+
393
+ function buildHybridContext7Requester({
394
+ lanePaths,
395
+ fetchImpl,
396
+ directApiKey,
397
+ directApiKeyEnvVar,
398
+ }) {
399
+ const brokerRequester = buildBrokerContext7Requester(fetchImpl, lanePaths);
400
+ let directRequester = null;
401
+ let activeProviderMode = "broker";
402
+ let fallbackWarning = "";
403
+
404
+ const resolveDirectRequester = () => {
405
+ if (directRequester) {
406
+ return directRequester;
407
+ }
408
+ if (!directApiKey) {
409
+ throw new Error(`${directApiKeyEnvVar} is not set; skipping Context7 prefetch.`);
410
+ }
411
+ directRequester = buildDirectContext7Requester(fetchImpl, directApiKey);
412
+ return directRequester;
413
+ };
414
+
415
+ const runWithFallback = async (method, params) => {
416
+ if (activeProviderMode === "direct") {
417
+ return resolveDirectRequester()[method](params);
418
+ }
419
+ try {
420
+ return await brokerRequester[method](params);
421
+ } catch (brokerError) {
422
+ let fallbackRequester = null;
423
+ try {
424
+ fallbackRequester = resolveDirectRequester();
425
+ } catch (fallbackUnavailableError) {
426
+ throw new Error(
427
+ `Context7 broker request failed and direct fallback is unavailable: ${brokerError instanceof Error ? brokerError.message : String(brokerError)}; ${fallbackUnavailableError instanceof Error ? fallbackUnavailableError.message : String(fallbackUnavailableError)}`,
428
+ );
429
+ }
430
+ activeProviderMode = "direct";
431
+ fallbackWarning =
432
+ fallbackWarning ||
433
+ `Context7 broker request failed; fell back to direct auth: ${brokerError instanceof Error ? brokerError.message : String(brokerError)}`;
434
+ try {
435
+ return await fallbackRequester[method](params);
436
+ } catch (fallbackError) {
437
+ throw new Error(
438
+ `Context7 broker request failed and direct fallback also failed: ${brokerError instanceof Error ? brokerError.message : String(brokerError)}; ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
439
+ );
440
+ }
441
+ }
442
+ };
443
+
444
+ return {
445
+ requester: {
446
+ search(params) {
447
+ return runWithFallback("search", params);
448
+ },
449
+ context(params) {
450
+ return runWithFallback("context", params);
451
+ },
452
+ },
453
+ providerMode: "broker",
454
+ getProviderMode() {
455
+ return activeProviderMode;
456
+ },
457
+ getWarning() {
458
+ return fallbackWarning;
459
+ },
460
+ };
461
+ }
462
+
463
+ function resolveContext7Requester({
464
+ lanePaths,
465
+ fetchImpl,
466
+ apiKey,
467
+ apiKeyEnvVar = DEFAULT_CONTEXT7_API_KEY_ENV_VAR,
468
+ }) {
469
+ const provider = lanePaths?.externalProviders?.context7 || {};
470
+ const mode = String(provider.mode || "direct").trim().toLowerCase();
471
+ const directApiKey = apiKey || process.env[provider.apiKeyEnvVar || apiKeyEnvVar] || "";
472
+ const direct = () => {
473
+ if (!directApiKey) {
474
+ throw new Error(`${provider.apiKeyEnvVar || apiKeyEnvVar} is not set; skipping Context7 prefetch.`);
475
+ }
476
+ return {
477
+ requester: buildDirectContext7Requester(fetchImpl, directApiKey),
478
+ providerMode: "direct",
479
+ };
480
+ };
481
+ const broker = () => ({
482
+ requester: buildBrokerContext7Requester(fetchImpl, lanePaths),
483
+ providerMode: "broker",
484
+ });
485
+ if (mode === "broker") {
486
+ return broker();
487
+ }
488
+ if (mode === "hybrid") {
489
+ try {
490
+ return buildHybridContext7Requester({
491
+ lanePaths,
492
+ fetchImpl,
493
+ directApiKey,
494
+ directApiKeyEnvVar: provider.apiKeyEnvVar || apiKeyEnvVar,
495
+ });
496
+ } catch {
497
+ return direct();
498
+ }
499
+ }
500
+ return direct();
501
+ }
502
+
503
+ async function resolveLibraryId(requester, library, selection) {
315
504
  if (library.libraryId) {
316
505
  return {
317
506
  libraryId: library.libraryId,
@@ -322,9 +511,7 @@ async function resolveLibraryId(fetchImpl, library, selection, apiKey) {
322
511
  libraryName: library.libraryName,
323
512
  query: selection.query || library.queryHint || library.libraryName,
324
513
  });
325
- const results = await requestContext7(fetchImpl, `${CONTEXT7_SEARCH_URL}?${params.toString()}`, {
326
- apiKey,
327
- });
514
+ const results = await requester.search(params);
328
515
  if (!Array.isArray(results) || results.length === 0) {
329
516
  throw new Error(`Context7 search returned no matches for "${library.libraryName}".`);
330
517
  }
@@ -334,8 +521,8 @@ async function resolveLibraryId(fetchImpl, library, selection, apiKey) {
334
521
  };
335
522
  }
336
523
 
337
- async function fetchLibraryContext(fetchImpl, library, selection, apiKey) {
338
- const resolvedLibrary = await resolveLibraryId(fetchImpl, library, selection, apiKey);
524
+ async function fetchLibraryContext(requester, library, selection) {
525
+ const resolvedLibrary = await resolveLibraryId(requester, library, selection);
339
526
  const query = compactSingleLine(
340
527
  [selection.query, library.queryHint].filter(Boolean).join(". Focus: "),
341
528
  320,
@@ -345,10 +532,7 @@ async function fetchLibraryContext(fetchImpl, library, selection, apiKey) {
345
532
  query,
346
533
  type: "txt",
347
534
  });
348
- const text = await requestContext7(fetchImpl, `${CONTEXT7_CONTEXT_URL}?${params.toString()}`, {
349
- apiKey,
350
- expectText: true,
351
- });
535
+ const text = await requester.context(params);
352
536
  return {
353
537
  libraryId: resolvedLibrary.libraryId,
354
538
  libraryName: resolvedLibrary.libraryName,
@@ -360,6 +544,7 @@ async function fetchLibraryContext(fetchImpl, library, selection, apiKey) {
360
544
  export async function prefetchContext7ForSelection(
361
545
  selection,
362
546
  {
547
+ lanePaths = null,
363
548
  cacheDir,
364
549
  apiKey = process.env.CONTEXT7_API_KEY || "",
365
550
  fetchImpl = globalThis.fetch,
@@ -397,13 +582,18 @@ export async function prefetchContext7ForSelection(
397
582
  };
398
583
  }
399
584
  if (!apiKey) {
400
- return {
401
- mode: "missing-key",
402
- selection,
403
- promptText: "",
404
- snippetHash: "",
405
- warning: "CONTEXT7_API_KEY is not set; skipping Context7 prefetch.",
406
- };
585
+ const providerMode = String(lanePaths?.externalProviders?.context7?.mode || "direct")
586
+ .trim()
587
+ .toLowerCase();
588
+ if (providerMode === "direct") {
589
+ return {
590
+ mode: "missing-key",
591
+ selection,
592
+ promptText: "",
593
+ snippetHash: "",
594
+ warning: "CONTEXT7_API_KEY is not set; skipping Context7 prefetch.",
595
+ };
596
+ }
407
597
  }
408
598
 
409
599
  ensureDirectory(cacheDir);
@@ -427,9 +617,15 @@ export async function prefetchContext7ForSelection(
427
617
  }
428
618
 
429
619
  try {
620
+ const requesterState = resolveContext7Requester({
621
+ lanePaths,
622
+ fetchImpl,
623
+ apiKey,
624
+ });
625
+ const { requester } = requesterState;
430
626
  const results = [];
431
627
  for (const library of selection.libraries) {
432
- const result = await fetchLibraryContext(fetchImpl, library, selection, apiKey);
628
+ const result = await fetchLibraryContext(requester, library, selection);
433
629
  if (result.text) {
434
630
  results.push(result);
435
631
  }
@@ -450,12 +646,18 @@ export async function prefetchContext7ForSelection(
450
646
  promptText,
451
647
  snippetHash,
452
648
  });
649
+ const providerMode =
650
+ typeof requesterState.getProviderMode === "function"
651
+ ? requesterState.getProviderMode()
652
+ : requesterState.providerMode;
653
+ const warning =
654
+ typeof requesterState.getWarning === "function" ? requesterState.getWarning() : "";
453
655
  return {
454
- mode: "fetched",
656
+ mode: providerMode === "broker" ? "fetched-broker" : "fetched",
455
657
  selection,
456
658
  promptText,
457
659
  snippetHash,
458
- warning: "",
660
+ warning,
459
661
  };
460
662
  } catch (error) {
461
663
  return {
@@ -42,11 +42,12 @@ import {
42
42
  import { readWaveRelaunchPlanSnapshot, readWaveRetryOverride, resolveRetryOverrideAgentIds, writeWaveRetryOverride, clearWaveRetryOverride } from "./retry-control.mjs";
43
43
  import { flushWaveControlQueue, readWaveControlQueueState } from "./wave-control-client.mjs";
44
44
  import { readAgentExecutionSummary, validateImplementationSummary } from "./agent-state.mjs";
45
- import { isContEvalReportOnlyAgent, isSecurityReviewAgent } from "./role-helpers.mjs";
45
+ import { isContEvalReportOnlyAgent, isSecurityReviewAgentForLane } from "./role-helpers.mjs";
46
46
  import {
47
47
  buildSignalStatusLine,
48
48
  syncWaveSignalProjections,
49
49
  } from "./signals.mjs";
50
+ import { summarizeSupervisorStateForWave } from "./supervisor-cli.mjs";
50
51
 
51
52
  function printUsage() {
52
53
  console.log(`Usage:
@@ -366,7 +367,8 @@ function buildLogicalAgents({
366
367
  proofRegistry || { entries: [] },
367
368
  );
368
369
  const proofValidation =
369
- !isSecurityReviewAgent(agent) && !isContEvalReportOnlyAgent(agent, { contEvalAgentId: lanePaths.contEvalAgentId })
370
+ !isSecurityReviewAgentForLane(agent, lanePaths) &&
371
+ !isContEvalReportOnlyAgent(agent, { contEvalAgentId: lanePaths.contEvalAgentId })
370
372
  ? validateImplementationSummary(agent, summary ? summary : null)
371
373
  : { ok: statusRecord?.code === 0, statusCode: statusRecord?.code === 0 ? "pass" : "pending" };
372
374
  const targetedTasks = tasks.filter(
@@ -385,7 +387,7 @@ function buildLogicalAgents({
385
387
  const satisfiedByStatus =
386
388
  statusRecord?.code === 0 &&
387
389
  (proofValidation.ok ||
388
- isSecurityReviewAgent(agent) ||
390
+ isSecurityReviewAgentForLane(agent, lanePaths) ||
389
391
  isContEvalReportOnlyAgent(agent, { contEvalAgentId: lanePaths.contEvalAgentId }));
390
392
  let state = "planned";
391
393
  let reason = "";
@@ -404,7 +406,7 @@ function buildLogicalAgents({
404
406
  lanePaths.integrationAgentId || "A8",
405
407
  lanePaths.documentationAgentId || "A9",
406
408
  lanePaths.contQaAgentId || "A0",
407
- ].includes(agent.agentId) || isSecurityReviewAgent(agent)
409
+ ].includes(agent.agentId) || isSecurityReviewAgentForLane(agent, lanePaths)
408
410
  ? "closed"
409
411
  : "satisfied";
410
412
  reason = "Completed wave preserves the latest satisfied agent state.";
@@ -425,7 +427,7 @@ function buildLogicalAgents({
425
427
  lanePaths.integrationAgentId || "A8",
426
428
  lanePaths.documentationAgentId || "A9",
427
429
  lanePaths.contQaAgentId || "A0",
428
- ].includes(agent.agentId) || isSecurityReviewAgent(agent)
430
+ ].includes(agent.agentId) || isSecurityReviewAgentForLane(agent, lanePaths)
429
431
  ? "closed"
430
432
  : "satisfied";
431
433
  reason = "Latest attempt satisfied current control-plane state.";
@@ -657,6 +659,12 @@ export function buildControlStatusPayload({ lanePaths, wave, agentId = "" }) {
657
659
  const controlState = readWaveControlPlaneState(lanePaths, wave.wave);
658
660
  const proofRegistry = readWaveProofRegistry(lanePaths, wave.wave) || { entries: [] };
659
661
  const relaunchPlan = readWaveRelaunchPlanSnapshot(lanePaths, wave.wave);
662
+ const supervisor = summarizeSupervisorStateForWave(lanePaths, wave.wave, {
663
+ agentId,
664
+ });
665
+ const forwardedClosureGaps = Array.isArray(relaunchPlan?.forwardedClosureGaps)
666
+ ? relaunchPlan.forwardedClosureGaps
667
+ : [];
660
668
  const rerunRequest = controlState.activeRerunRequest
661
669
  ? {
662
670
  ...controlState.activeRerunRequest,
@@ -718,6 +726,8 @@ export function buildControlStatusPayload({ lanePaths, wave, agentId = "" }) {
718
726
  selectionSource: selection.source,
719
727
  rerunRequest,
720
728
  relaunchPlan,
729
+ forwardedClosureGaps,
730
+ supervisor,
721
731
  nextTimer: isCompletedPhase(phase) ? null : nextTaskDeadline(tasks),
722
732
  activeAttempt: controlState.activeAttempt,
723
733
  };
@@ -758,6 +768,33 @@ function printStatus(payload) {
758
768
  console.log(buildSignalStatusLine(payload.signals.wave, payload));
759
769
  }
760
770
  console.log(`blocking=${blocking}`);
771
+ if (payload.supervisor) {
772
+ console.log(
773
+ `supervisor=${payload.supervisor.terminalDisposition || payload.supervisor.status} run_id=${payload.supervisor.runId} launcher_pid=${payload.supervisor.launcherPid || "none"}`,
774
+ );
775
+ if (payload.supervisor.sessionBackend || payload.supervisor.recoveryState || payload.supervisor.resumeAction) {
776
+ console.log(
777
+ `supervisor-backend=${payload.supervisor.sessionBackend || "unknown"} recovery=${payload.supervisor.recoveryState || "unknown"} resume=${payload.supervisor.resumeAction || "none"}`,
778
+ );
779
+ }
780
+ if ((payload.supervisor.agentRuntimeSummary || []).length > 0) {
781
+ console.log("supervisor-runtime:");
782
+ for (const record of payload.supervisor.agentRuntimeSummary) {
783
+ console.log(
784
+ `- ${record.agentId || "unknown"} ${record.terminalDisposition || "unknown"} pid=${record.pid || "none"} backend=${record.sessionBackend || "process"} attach=${record.attachMode || "log-tail"} heartbeat=${record.lastHeartbeatAt || "n/a"}`,
785
+ );
786
+ }
787
+ }
788
+ }
789
+ if ((payload.forwardedClosureGaps || []).length > 0) {
790
+ console.log("forwarded-closure-gaps:");
791
+ for (const gap of payload.forwardedClosureGaps) {
792
+ const targets = Array.isArray(gap.targets) && gap.targets.length > 0 ? gap.targets.join(",") : "none";
793
+ console.log(
794
+ `- ${gap.stageKey} agent=${gap.agentId || "unknown"} attempt=${gap.attempt ?? "n/a"} targets=${targets}${gap.detail ? ` detail=${gap.detail}` : ""}`,
795
+ );
796
+ }
797
+ }
761
798
  if (payload.nextTimer) {
762
799
  console.log(`next-timer=${payload.nextTimer.kind} ${payload.nextTimer.taskId} at ${payload.nextTimer.at}`);
763
800
  }
@@ -197,6 +197,8 @@ export function buildExecutionPrompt({
197
197
  inboxPath = null,
198
198
  inboxText = "",
199
199
  context7 = null,
200
+ corridorContextPath = null,
201
+ corridorContextText = "",
200
202
  componentPromotions = null,
201
203
  evalTargets = null,
202
204
  benchmarkCatalogPath = null,
@@ -215,6 +217,9 @@ export function buildExecutionPrompt({
215
217
  ? path.relative(REPO_ROOT, sharedSummaryPath)
216
218
  : null;
217
219
  const relativeInboxPath = inboxPath ? path.relative(REPO_ROOT, inboxPath) : null;
220
+ const relativeCorridorContextPath = corridorContextPath
221
+ ? path.relative(REPO_ROOT, corridorContextPath)
222
+ : null;
218
223
  const relativeSignalStatePath = signalStatePath
219
224
  ? path.relative(REPO_ROOT, signalStatePath)
220
225
  : null;
@@ -537,6 +542,12 @@ export function buildExecutionPrompt({
537
542
  `Agent inbox repo-relative path: ${relativeInboxPath}`,
538
543
  ]
539
544
  : []),
545
+ ...(corridorContextPath
546
+ ? [
547
+ `Corridor context absolute path: ${corridorContextPath}`,
548
+ `Corridor context repo-relative path: ${relativeCorridorContextPath}`,
549
+ ]
550
+ : []),
540
551
  ...(signalStatePath
541
552
  ? [
542
553
  `Signal state absolute path: ${signalStatePath}`,
@@ -552,6 +563,9 @@ export function buildExecutionPrompt({
552
563
  ...(inboxText
553
564
  ? ["Current agent inbox:", "```markdown", inboxText, "```", ""]
554
565
  : []),
566
+ ...(corridorContextText
567
+ ? ["Current Corridor context:", "```text", corridorContextText, "```", ""]
568
+ : []),
555
569
  ...(signalStatePath
556
570
  ? [
557
571
  "Long-running signal loop:",