@chllming/wave-orchestration 0.8.5 → 0.8.6

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 (38) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +14 -9
  3. package/docs/README.md +3 -1
  4. package/docs/context7/bundles.json +19 -20
  5. package/docs/context7/planner-agent/README.md +4 -1
  6. package/docs/guides/author-and-run-waves.md +4 -1
  7. package/docs/guides/planner.md +3 -1
  8. package/docs/guides/signal-wrappers.md +165 -0
  9. package/docs/guides/terminal-surfaces.md +13 -0
  10. package/docs/plans/context7-wave-orchestrator.md +24 -7
  11. package/docs/plans/current-state.md +7 -3
  12. package/docs/plans/end-state-architecture.md +16 -4
  13. package/docs/plans/examples/wave-example-design-handoff.md +1 -1
  14. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  15. package/docs/plans/migration.md +165 -72
  16. package/docs/plans/wave-orchestrator.md +3 -0
  17. package/docs/reference/cli-reference.md +11 -2
  18. package/docs/reference/npmjs-trusted-publishing.md +2 -2
  19. package/docs/reference/sample-waves.md +5 -5
  20. package/docs/reference/skills.md +9 -1
  21. package/docs/reference/wave-control.md +2 -0
  22. package/package.json +1 -1
  23. package/releases/manifest.json +19 -0
  24. package/scripts/context7-api-check.sh +57 -13
  25. package/scripts/wave-orchestrator/control-cli.mjs +19 -0
  26. package/scripts/wave-orchestrator/coordination.mjs +35 -0
  27. package/scripts/wave-orchestrator/install.mjs +2 -0
  28. package/scripts/wave-orchestrator/launcher-runtime.mjs +11 -0
  29. package/scripts/wave-orchestrator/launcher.mjs +20 -0
  30. package/scripts/wave-orchestrator/session-supervisor.mjs +113 -0
  31. package/scripts/wave-orchestrator/shared.mjs +1 -0
  32. package/scripts/wave-orchestrator/signals.mjs +681 -0
  33. package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
  34. package/scripts/wave-status.sh +200 -0
  35. package/scripts/wave-watch.sh +200 -0
  36. package/skills/README.md +3 -0
  37. package/skills/signal-hygiene/SKILL.md +51 -0
  38. package/skills/signal-hygiene/skill.json +20 -0
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env bash
2
- # Minimal Context7 API smoke test (expects CONTEXT7_API_KEY in the environment).
3
2
  set -euo pipefail
4
3
 
5
4
  if [[ -z "${CONTEXT7_API_KEY:-}" ]]; then
@@ -7,15 +6,60 @@ if [[ -z "${CONTEXT7_API_KEY:-}" ]]; then
7
6
  exit 1
8
7
  fi
9
8
 
10
- URL='https://context7.com/api/v2/libs/search?libraryName=temporal&query=go%20workflow'
11
- echo "GET $URL" >&2
12
- RESP="$(curl -fsS "$URL" -H "Authorization: Bearer ${CONTEXT7_API_KEY}" -H "Accept: application/json")"
13
- RESP_JSON="$RESP" node -e "
14
- const j = JSON.parse(process.env.RESP_JSON || '{}');
15
- const list = Array.isArray(j) ? j : (j.results ?? j.items ?? []);
16
- const first = list[0];
17
- if (!first) { console.error('Unexpected response shape:', Object.keys(j)); process.exit(1); }
18
- const id = first.id ?? first.libraryId;
19
- const name = first.title ?? first.name;
20
- console.log('ok — first library:', id || name || JSON.stringify(first).slice(0, 120));
21
- "
9
+ node <<'NODE'
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+
13
+ const apiKey = process.env.CONTEXT7_API_KEY || "";
14
+ const repoRoot = process.cwd();
15
+ const bundlePath = path.join(repoRoot, "docs/context7/bundles.json");
16
+ const payload = JSON.parse(fs.readFileSync(bundlePath, "utf8"));
17
+
18
+ const entries = [];
19
+ for (const [bundleId, bundle] of Object.entries(payload.bundles || {})) {
20
+ for (const library of bundle.libraries || []) {
21
+ if (!library.libraryId) {
22
+ throw new Error(
23
+ `Bundle "${bundleId}" must pin exact Context7 libraryId values. Found libraryName=${JSON.stringify(library.libraryName || "")}.`,
24
+ );
25
+ }
26
+ entries.push({
27
+ bundleId,
28
+ libraryId: String(library.libraryId),
29
+ queryHint: String(library.queryHint || "overview"),
30
+ });
31
+ }
32
+ }
33
+
34
+ const uniqueEntries = [...new Map(entries.map((entry) => [entry.libraryId, entry])).values()];
35
+
36
+ async function validate(entry) {
37
+ const url = new URL("https://context7.com/api/v2/context");
38
+ url.searchParams.set("libraryId", entry.libraryId);
39
+ url.searchParams.set("query", entry.queryHint);
40
+ url.searchParams.set("type", "txt");
41
+ const response = await fetch(url, {
42
+ headers: {
43
+ Authorization: "Bearer " + apiKey,
44
+ Accept: "text/plain, application/json",
45
+ },
46
+ });
47
+ const text = await response.text();
48
+ if (!response.ok) {
49
+ throw new Error(`Context7 ${entry.libraryId} failed (${response.status}): ${text.slice(0, 200)}`);
50
+ }
51
+ if (text.trim().length === 0) {
52
+ throw new Error(`Context7 ${entry.libraryId} returned empty context.`);
53
+ }
54
+ console.log(`ok -- ${entry.libraryId} (${entry.bundleId})`);
55
+ }
56
+
57
+ (async () => {
58
+ for (const entry of uniqueEntries) {
59
+ await validate(entry);
60
+ }
61
+ })().catch((error) => {
62
+ console.error(error instanceof Error ? error.message : String(error));
63
+ process.exit(1);
64
+ });
65
+ NODE
@@ -36,6 +36,10 @@ import { readWaveRelaunchPlanSnapshot, readWaveRetryOverride, resolveRetryOverri
36
36
  import { flushWaveControlQueue, readWaveControlQueueState } from "./wave-control-client.mjs";
37
37
  import { readAgentExecutionSummary, validateImplementationSummary } from "./agent-state.mjs";
38
38
  import { isContEvalReportOnlyAgent, isSecurityReviewAgent } from "./role-helpers.mjs";
39
+ import {
40
+ buildSignalStatusLine,
41
+ syncWaveSignalProjections,
42
+ } from "./signals.mjs";
39
43
 
40
44
  function printUsage() {
41
45
  console.log(`Usage:
@@ -676,6 +680,7 @@ export function buildControlStatusPayload({ lanePaths, wave, agentId = "" }) {
676
680
  proofBundles: (proofRegistry?.entries || []).filter(
677
681
  (entry) => !agentId || entry.agentId === agentId,
678
682
  ),
683
+ feedbackRequests,
679
684
  selectionSource: selection.source,
680
685
  rerunRequest,
681
686
  relaunchPlan,
@@ -690,6 +695,7 @@ function ensureWaveStateDirs(lanePaths) {
690
695
  ensureDirectory(lanePaths.controlPlaneDir);
691
696
  ensureDirectory(lanePaths.assignmentsDir);
692
697
  ensureDirectory(lanePaths.inboxesDir);
698
+ ensureDirectory(lanePaths.signalsDir);
693
699
  ensureDirectory(lanePaths.messageboardsDir);
694
700
  ensureDirectory(lanePaths.docsQueueDir);
695
701
  ensureDirectory(lanePaths.ledgerDir);
@@ -714,6 +720,9 @@ function printStatus(payload) {
714
720
  ? `${payload.blockingEdge.kind} ${payload.blockingEdge.id}: ${payload.blockingEdge.detail}`
715
721
  : "none";
716
722
  console.log(`lane=${payload.lane} wave=${payload.wave} phase=${payload.phase}`);
723
+ if (payload.signals?.wave) {
724
+ console.log(buildSignalStatusLine(payload.signals.wave, payload));
725
+ }
717
726
  console.log(`blocking=${blocking}`);
718
727
  if (payload.nextTimer) {
719
728
  console.log(`next-timer=${payload.nextTimer.kind} ${payload.nextTimer.taskId} at ${payload.nextTimer.at}`);
@@ -901,6 +910,16 @@ export async function runControlCli(argv) {
901
910
  wave,
902
911
  agentId: options.agent || "",
903
912
  });
913
+ const signalSync = syncWaveSignalProjections({
914
+ lanePaths,
915
+ wave,
916
+ statusPayload: payload,
917
+ includeResident: Boolean(options.orchestratorId),
918
+ });
919
+ payload.signals = {
920
+ wave: signalSync.wave?.snapshot || null,
921
+ agents: (signalSync.agents || []).map((entry) => entry.snapshot),
922
+ };
904
923
  if (options.json) {
905
924
  console.log(JSON.stringify(payload, null, 2));
906
925
  } else {
@@ -203,6 +203,8 @@ export function buildExecutionPrompt({
203
203
  sharedPlanDocs = null,
204
204
  designPacketPaths = null,
205
205
  designExecutionMode = null,
206
+ signalStatePath = null,
207
+ signalAckPath = null,
206
208
  contQaAgentId = "A0",
207
209
  contEvalAgentId = "E0",
208
210
  integrationAgentId = "A8",
@@ -213,6 +215,10 @@ export function buildExecutionPrompt({
213
215
  ? path.relative(REPO_ROOT, sharedSummaryPath)
214
216
  : null;
215
217
  const relativeInboxPath = inboxPath ? path.relative(REPO_ROOT, inboxPath) : null;
218
+ const relativeSignalStatePath = signalStatePath
219
+ ? path.relative(REPO_ROOT, signalStatePath)
220
+ : null;
221
+ const relativeSignalAckPath = signalAckPath ? path.relative(REPO_ROOT, signalAckPath) : null;
216
222
  const lanePlansDir = lane === DEFAULT_WAVE_LANE ? "docs/plans" : `docs/${lane}/plans`;
217
223
  const resolvedSharedPlanDocs =
218
224
  sharedPlanDocs && sharedPlanDocs.length > 0
@@ -531,6 +537,14 @@ export function buildExecutionPrompt({
531
537
  `Agent inbox repo-relative path: ${relativeInboxPath}`,
532
538
  ]
533
539
  : []),
540
+ ...(signalStatePath
541
+ ? [
542
+ `Signal state absolute path: ${signalStatePath}`,
543
+ `Signal state repo-relative path: ${relativeSignalStatePath}`,
544
+ `Signal ack absolute path: ${signalAckPath}`,
545
+ `Signal ack repo-relative path: ${relativeSignalAckPath}`,
546
+ ]
547
+ : []),
534
548
  "",
535
549
  ...(sharedSummaryText
536
550
  ? ["Current wave shared summary:", "```markdown", sharedSummaryText, "```", ""]
@@ -538,6 +552,17 @@ export function buildExecutionPrompt({
538
552
  ...(inboxText
539
553
  ? ["Current agent inbox:", "```markdown", inboxText, "```", ""]
540
554
  : []),
555
+ ...(signalStatePath
556
+ ? [
557
+ "Long-running signal loop:",
558
+ "- If you are operating as a resident or waiting agent, keep watching the signal state JSON instead of exiting early.",
559
+ "- When the signal `version` increases beyond the version recorded in the signal ack file, immediately write the ack file before acting.",
560
+ `- Write the ack file as JSON with exactly these keys: \`agentId\`, \`version\`, \`signal\`, and \`observedAt\`. Use \`${agent.agentId}\` as \`agentId\` and an ISO-8601 timestamp for \`observedAt\`.`,
561
+ "- After writing the ack, re-read the inbox, shared summary, and message board, then handle the new signal once.",
562
+ "- If the signal version has not changed, stay idle. Do not busy-loop or repeat unchanged work.",
563
+ "",
564
+ ]
565
+ : []),
541
566
  ...exitContractLines,
542
567
  ...promotedComponentLines,
543
568
  ...evalTargetLines,
@@ -564,6 +589,8 @@ export function buildResidentOrchestratorPrompt({
564
589
  sharedSummaryPath,
565
590
  dashboardPath,
566
591
  triagePath = null,
592
+ signalStatePath = null,
593
+ signalAckPath = null,
567
594
  rolePrompt = "",
568
595
  }) {
569
596
  const coordinationCommand = [
@@ -612,6 +639,8 @@ export function buildResidentOrchestratorPrompt({
612
639
  `- Wave dashboard: ${dashboardPath}`,
613
640
  `- Message board projection: ${messageBoardPath}`,
614
641
  ...(triagePath ? [`- Feedback triage log: ${triagePath}`] : []),
642
+ ...(signalStatePath ? [`- Signal state: ${signalStatePath}`] : []),
643
+ ...(signalAckPath ? [`- Signal ack: ${signalAckPath}`] : []),
615
644
  "",
616
645
  "Action surface:",
617
646
  `- Coordination command: \`${coordinationCommand}\``,
@@ -624,6 +653,12 @@ export function buildResidentOrchestratorPrompt({
624
653
  "2. Identify open clarifications, open clarification-linked requests, overdue acknowledgements, and human-feedback state.",
625
654
  "3. If action is needed, write a durable coordination update and explain the policy basis for the action.",
626
655
  "4. If nothing needs action, continue monitoring. Do not exit until the wave is clearly terminal or the launcher stops the session.",
656
+ ...(signalStatePath
657
+ ? [
658
+ "5. When the signal state `version` increases, immediately write the signal ack file before taking action so the launcher knows you observed the change.",
659
+ "6. After acknowledging the signal, re-read the shared summary, dashboard, coordination log, and triage artifacts before intervening.",
660
+ ]
661
+ : []),
627
662
  "",
628
663
  ...(roleSection
629
664
  ? [
@@ -27,6 +27,8 @@ export const CHANGELOG_MANIFEST_PATH = path.join(PACKAGE_ROOT, "releases", "mani
27
27
  export const WORKSPACE_PACKAGE_JSON_PATH = path.join(REPO_ROOT, "package.json");
28
28
  export const STARTER_TEMPLATE_PATHS = [
29
29
  "wave.config.json",
30
+ "scripts/wave-status.sh",
31
+ "scripts/wave-watch.sh",
30
32
  "docs/README.md",
31
33
  "docs/agents/wave-documentation-role.md",
32
34
  "docs/agents/wave-design-role.md",
@@ -20,6 +20,11 @@ import {
20
20
  summarizeResolvedSkills,
21
21
  writeResolvedSkillArtifacts,
22
22
  } from "./skills.mjs";
23
+ import {
24
+ agentSignalAckPath,
25
+ agentSignalPath,
26
+ agentUsesSignalHygiene,
27
+ } from "./signals.mjs";
23
28
 
24
29
  export function refreshResolvedSkillsForRun(runInfo, waveDefinition, lanePaths) {
25
30
  runInfo.agent.skillsResolved = resolveAgentSkills(
@@ -129,6 +134,12 @@ export async function launchAgentSession(lanePaths, params, { runTmuxFn }) {
129
134
  .map((waveAgent) => resolveDesignReportPath(waveAgent))
130
135
  .filter(Boolean),
131
136
  designExecutionMode,
137
+ signalStatePath: agentUsesSignalHygiene(agent)
138
+ ? agentSignalPath(lanePaths, wave, agent.agentId)
139
+ : null,
140
+ signalAckPath: agentUsesSignalHygiene(agent)
141
+ ? agentSignalAckPath(lanePaths, wave, agent.agentId)
142
+ : null,
132
143
  });
133
144
  const promptHash = hashAgentPromptFingerprint(agent);
134
145
  fs.writeFileSync(promptPath, `${prompt}\n`, "utf8");
@@ -209,7 +209,9 @@ import {
209
209
  recordAttemptState,
210
210
  recordWaveRunState,
211
211
  runTmux,
212
+ syncLiveWaveSignals,
212
213
  } from "./session-supervisor.mjs";
214
+ import { buildControlStatusPayload } from "./control-cli.mjs";
213
215
  import {
214
216
  planInitialWaveAttempt,
215
217
  planRetryWaveAttempt,
@@ -624,6 +626,7 @@ export async function runLauncherCli(argv) {
624
626
  ensureDirectory(lanePaths.controlDir);
625
627
  ensureDirectory(lanePaths.assignmentsDir);
626
628
  ensureDirectory(lanePaths.inboxesDir);
629
+ ensureDirectory(lanePaths.signalsDir);
627
630
  ensureDirectory(lanePaths.ledgerDir);
628
631
  ensureDirectory(lanePaths.integrationDir);
629
632
  ensureDirectory(lanePaths.proofDir);
@@ -1188,6 +1191,19 @@ export async function runLauncherCli(argv) {
1188
1191
  flushDashboards();
1189
1192
  return true;
1190
1193
  };
1194
+ const syncWaveSignals = () =>
1195
+ syncLiveWaveSignals({
1196
+ lanePaths,
1197
+ wave,
1198
+ statusPayload: buildControlStatusPayload({
1199
+ lanePaths,
1200
+ wave,
1201
+ }),
1202
+ agentRuns,
1203
+ residentEnabled: Boolean(residentOrchestratorRun),
1204
+ recordCombinedEvent,
1205
+ appendCoordination,
1206
+ });
1191
1207
 
1192
1208
  const proofRegistryForReuse = readWaveProofRegistry(lanePaths, wave.wave);
1193
1209
  const initialAttemptPlan = planInitialWaveAttempt({
@@ -1217,6 +1233,7 @@ export async function runLauncherCli(argv) {
1217
1233
  }
1218
1234
  flushDashboards();
1219
1235
  emitCoordinationAlertEvents(derivedState);
1236
+ syncWaveSignals();
1220
1237
 
1221
1238
  if (options.dashboard && currentWaveDashboardTerminalEntry) {
1222
1239
  launchWaveDashboardSession(lanePaths, {
@@ -1281,6 +1298,7 @@ export async function runLauncherCli(argv) {
1281
1298
  details: `session=${residentOrchestratorRun.sessionName}; executor=${residentOrchestratorRun.lastExecutorId || "unknown"}`,
1282
1299
  actionRequested: "None",
1283
1300
  });
1301
+ syncWaveSignals();
1284
1302
  }
1285
1303
  }
1286
1304
 
@@ -1487,6 +1505,7 @@ export async function runLauncherCli(argv) {
1487
1505
  updateWaveDashboardMessageBoard(dashboardState, messageBoardPath);
1488
1506
  flushDashboards();
1489
1507
  }
1508
+ syncWaveSignals();
1490
1509
  },
1491
1510
  {
1492
1511
  controlPlane: {
@@ -1501,6 +1520,7 @@ export async function runLauncherCli(argv) {
1501
1520
 
1502
1521
  materializeAgentExecutionSummaries(wave, agentRuns);
1503
1522
  refreshDerivedState(attempt);
1523
+ syncWaveSignals();
1504
1524
  lastLiveCoordinationRefreshAt = Date.now();
1505
1525
  emitCoordinationAlertEvents(derivedState);
1506
1526
  failures = reconcileFailuresAgainstSharedComponentState(wave, agentRuns, failures);
@@ -40,6 +40,13 @@ import {
40
40
  launchAgentSession as launchAgentSessionImpl,
41
41
  waitForWaveCompletion as waitForWaveCompletionImpl,
42
42
  } from "./launcher-runtime.mjs";
43
+ import {
44
+ agentUsesSignalHygiene,
45
+ buildSignalStatusLine,
46
+ residentSignalAckPath,
47
+ residentSignalPath,
48
+ syncWaveSignalProjections,
49
+ } from "./signals.mjs";
43
50
 
44
51
  function isProcessAlive(pid) {
45
52
  if (!Number.isInteger(pid) || pid <= 0) {
@@ -363,6 +370,8 @@ export function buildResidentOrchestratorRun({
363
370
  sharedSummaryPath: derivedState.sharedSummaryPath,
364
371
  dashboardPath,
365
372
  triagePath: derivedState.clarificationTriage?.triagePath || null,
373
+ signalStatePath: residentSignalPath(lanePaths, wave.wave),
374
+ signalAckPath: residentSignalAckPath(lanePaths, wave.wave),
366
375
  rolePrompt: agent.prompt,
367
376
  }),
368
377
  },
@@ -777,3 +786,107 @@ export function monitorWaveHumanFeedback({
777
786
  }
778
787
  return changed;
779
788
  }
789
+
790
+ export function syncLiveWaveSignals({
791
+ lanePaths,
792
+ wave,
793
+ statusPayload,
794
+ agentRuns,
795
+ residentEnabled = false,
796
+ recordCombinedEvent,
797
+ appendCoordination,
798
+ }) {
799
+ const activeSignalAgents = new Set(
800
+ (Array.isArray(agentRuns) ? agentRuns : [])
801
+ .filter((run) => agentUsesSignalHygiene(run?.agent))
802
+ .map((run) => run.agent.agentId),
803
+ );
804
+ const syncResult = syncWaveSignalProjections({
805
+ lanePaths,
806
+ wave,
807
+ statusPayload,
808
+ includeResident: residentEnabled,
809
+ });
810
+ if (syncResult.wave?.changed) {
811
+ appendWaveControlEvent(lanePaths, wave.wave, {
812
+ entityType: "wave_signal",
813
+ entityId: `wave-${wave.wave}`,
814
+ action: "updated",
815
+ source: "session-supervisor",
816
+ actor: "session-supervisor",
817
+ data: syncResult.wave.snapshot,
818
+ });
819
+ if (typeof recordCombinedEvent === "function") {
820
+ recordCombinedEvent({
821
+ level: syncResult.wave.snapshot.shouldWake ? "warn" : "info",
822
+ message: `Wave signal updated: ${buildSignalStatusLine(syncResult.wave.snapshot)}`,
823
+ });
824
+ }
825
+ }
826
+ for (const agentResult of syncResult.agents || []) {
827
+ if (!agentResult.changed) {
828
+ continue;
829
+ }
830
+ appendWaveControlEvent(lanePaths, wave.wave, {
831
+ entityType: "agent_signal",
832
+ entityId: `wave-${wave.wave}-agent-${agentResult.agentId}`,
833
+ action: "updated",
834
+ source: "session-supervisor",
835
+ actor: "session-supervisor",
836
+ data: agentResult.snapshot,
837
+ });
838
+ if (
839
+ agentResult.snapshot?.shouldWake &&
840
+ activeSignalAgents.has(agentResult.agentId) &&
841
+ typeof recordCombinedEvent === "function"
842
+ ) {
843
+ recordCombinedEvent({
844
+ level: "info",
845
+ agentId: agentResult.agentId,
846
+ message: `Signal changed: ${buildSignalStatusLine(agentResult.snapshot, {
847
+ lane: lanePaths.lane,
848
+ wave: wave.wave,
849
+ agentId: agentResult.agentId,
850
+ })}`,
851
+ });
852
+ }
853
+ }
854
+ if (syncResult.resident?.changed) {
855
+ appendWaveControlEvent(lanePaths, wave.wave, {
856
+ entityType: "agent_signal",
857
+ entityId: `wave-${wave.wave}-agent-resident-orchestrator`,
858
+ action: "updated",
859
+ source: "session-supervisor",
860
+ actor: "session-supervisor",
861
+ data: syncResult.resident.snapshot,
862
+ });
863
+ if (syncResult.resident.snapshot?.shouldWake && typeof recordCombinedEvent === "function") {
864
+ recordCombinedEvent({
865
+ level: "info",
866
+ agentId: "ORCH",
867
+ message: `Resident orchestrator signal changed: ${buildSignalStatusLine(
868
+ syncResult.resident.snapshot,
869
+ {
870
+ lane: lanePaths.lane,
871
+ wave: wave.wave,
872
+ agentId: "resident-orchestrator",
873
+ },
874
+ )}`,
875
+ });
876
+ }
877
+ if (
878
+ syncResult.resident.snapshot?.shouldWake &&
879
+ typeof appendCoordination === "function"
880
+ ) {
881
+ appendCoordination({
882
+ event: "resident_signal_updated",
883
+ waves: [wave.wave],
884
+ status: "running",
885
+ details: syncResult.resident.snapshot.reason || syncResult.resident.snapshot.signal,
886
+ actionRequested:
887
+ "Resident orchestrator should re-read the signal snapshot, shared summary, dashboard, and coordination log.",
888
+ });
889
+ }
890
+ }
891
+ return syncResult;
892
+ }
@@ -199,6 +199,7 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
199
199
  telemetryDir: path.join(stateDir, "control-plane", "telemetry"),
200
200
  assignmentsDir: path.join(stateDir, "assignments"),
201
201
  inboxesDir: path.join(stateDir, "inboxes"),
202
+ signalsDir: path.join(stateDir, "signals"),
202
203
  ledgerDir: path.join(stateDir, "ledger"),
203
204
  integrationDir: path.join(stateDir, "integration"),
204
205
  resultsDir: path.join(stateDir, "results"),