@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.
- package/CHANGELOG.md +24 -0
- package/README.md +14 -9
- package/docs/README.md +3 -1
- package/docs/context7/bundles.json +19 -20
- package/docs/context7/planner-agent/README.md +4 -1
- package/docs/guides/author-and-run-waves.md +4 -1
- package/docs/guides/planner.md +3 -1
- package/docs/guides/signal-wrappers.md +165 -0
- package/docs/guides/terminal-surfaces.md +13 -0
- package/docs/plans/context7-wave-orchestrator.md +24 -7
- package/docs/plans/current-state.md +7 -3
- package/docs/plans/end-state-architecture.md +16 -4
- 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 +165 -72
- package/docs/plans/wave-orchestrator.md +3 -0
- package/docs/reference/cli-reference.md +11 -2
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/sample-waves.md +5 -5
- package/docs/reference/skills.md +9 -1
- package/docs/reference/wave-control.md +2 -0
- package/package.json +1 -1
- package/releases/manifest.json +19 -0
- package/scripts/context7-api-check.sh +57 -13
- package/scripts/wave-orchestrator/control-cli.mjs +19 -0
- package/scripts/wave-orchestrator/coordination.mjs +35 -0
- package/scripts/wave-orchestrator/install.mjs +2 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +11 -0
- package/scripts/wave-orchestrator/launcher.mjs +20 -0
- package/scripts/wave-orchestrator/session-supervisor.mjs +113 -0
- package/scripts/wave-orchestrator/shared.mjs +1 -0
- package/scripts/wave-orchestrator/signals.mjs +681 -0
- package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
- package/scripts/wave-status.sh +200 -0
- package/scripts/wave-watch.sh +200 -0
- package/skills/README.md +3 -0
- package/skills/signal-hygiene/SKILL.md +51 -0
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
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"),
|