@chllming/wave-orchestration 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -1
- package/LICENSE.md +21 -0
- package/README.md +20 -9
- package/docs/README.md +8 -4
- package/docs/agents/wave-security-role.md +1 -0
- package/docs/architecture/README.md +1 -1
- package/docs/concepts/operating-modes.md +1 -1
- package/docs/guides/author-and-run-waves.md +1 -1
- package/docs/guides/planner.md +2 -2
- package/docs/guides/{recommendations-0.9.1.md → recommendations-0.9.2.md} +7 -7
- package/docs/guides/recommendations-0.9.3.md +137 -0
- package/docs/guides/sandboxed-environments.md +2 -2
- package/docs/plans/current-state.md +8 -2
- package/docs/plans/end-state-architecture.md +1 -1
- package/docs/plans/examples/wave-example-design-handoff.md +1 -1
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +65 -67
- package/docs/reference/cli-reference.md +1 -1
- package/docs/reference/coordination-and-closure.md +20 -3
- package/docs/reference/corridor.md +225 -0
- package/docs/reference/npmjs-token-publishing.md +2 -2
- package/docs/reference/package-publishing-flow.md +11 -11
- package/docs/reference/runtime-config/README.md +61 -3
- package/docs/reference/sample-waves.md +5 -5
- package/docs/reference/skills.md +1 -1
- package/docs/reference/wave-control.md +358 -27
- package/docs/roadmap.md +12 -19
- package/package.json +1 -1
- package/releases/manifest.json +44 -3
- package/scripts/wave-cli-bootstrap.mjs +52 -1
- package/scripts/wave-orchestrator/agent-state.mjs +26 -9
- package/scripts/wave-orchestrator/config.mjs +199 -3
- package/scripts/wave-orchestrator/context7.mjs +231 -29
- package/scripts/wave-orchestrator/coordination.mjs +15 -1
- package/scripts/wave-orchestrator/corridor.mjs +363 -0
- package/scripts/wave-orchestrator/derived-state-engine.mjs +38 -1
- package/scripts/wave-orchestrator/gate-engine.mjs +20 -0
- package/scripts/wave-orchestrator/install.mjs +34 -1
- package/scripts/wave-orchestrator/launcher-runtime.mjs +111 -7
- package/scripts/wave-orchestrator/launcher.mjs +21 -3
- package/scripts/wave-orchestrator/planner.mjs +30 -0
- package/scripts/wave-orchestrator/projection-writer.mjs +23 -0
- package/scripts/wave-orchestrator/provider-runtime.mjs +104 -0
- package/scripts/wave-orchestrator/shared.mjs +1 -0
- package/scripts/wave-orchestrator/traces.mjs +25 -0
- package/scripts/wave-orchestrator/wave-control-client.mjs +14 -1
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
338
|
-
const resolvedLibrary = await resolveLibraryId(
|
|
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
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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(
|
|
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 {
|
|
@@ -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;
|
|
@@ -248,7 +253,7 @@ export function buildExecutionPrompt({
|
|
|
248
253
|
? [
|
|
249
254
|
`- Because you are Agent ${contQaAgentId}, your cont-QA report must end with exactly one standalone line in the form \`Verdict: PASS\`, \`Verdict: CONCERNS\`, or \`Verdict: BLOCKED\`.`,
|
|
250
255
|
"- Also emit one matching structured marker in your terminal output: `[wave-verdict] pass`, `[wave-verdict] concerns`, or `[wave-verdict] blocked`.",
|
|
251
|
-
"- Emit one final structured gate marker: `[wave-gate] architecture=<pass|concerns|blocked> integration=<pass|concerns|blocked> durability=<pass|concerns|blocked> live=<pass|concerns|blocked> docs=<pass|concerns|blocked> detail=<short-note>`.",
|
|
256
|
+
"- Emit one final structured gate marker: `[wave-gate] architecture=<pass|concerns|blocked|gap> integration=<pass|concerns|blocked|gap> durability=<pass|concerns|blocked|gap> live=<pass|concerns|blocked|gap> docs=<pass|concerns|blocked|gap> detail=<short-note>`.",
|
|
252
257
|
"- Only use `Verdict: PASS` when the wave is coherent enough to unblock the next wave.",
|
|
253
258
|
`- Do not declare PASS until the documentation gate is closed: impacted implementation-owned docs must exist, ${sharedPlanDocList} must reflect plan-affecting outcomes, and no unresolved architecture-versus-plans drift remains.`,
|
|
254
259
|
"- If shared-plan reconciliation is still active inside the wave, require the exact remaining doc delta and an explicit `closed` or `no-change` note from the documentation steward or named owner before finalizing. Do not treat ownership handoff alone as the blocker.",
|
|
@@ -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:",
|