@chllming/wave-orchestration 0.8.5 → 0.8.7
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 +46 -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 +15 -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 +179 -72
- package/docs/plans/wave-orchestrator.md +11 -5
- package/docs/reference/cli-reference.md +21 -4
- package/docs/reference/coordination-and-closure.md +26 -5
- package/docs/reference/live-proof-waves.md +9 -0
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/runtime-config/README.md +9 -3
- package/docs/reference/sample-waves.md +5 -5
- package/docs/reference/skills.md +9 -1
- package/docs/reference/wave-control.md +18 -0
- package/docs/reference/wave-planning-lessons.md +7 -1
- package/docs/research/coordination-failure-review.md +6 -6
- package/package.json +1 -1
- package/releases/manifest.json +38 -0
- package/scripts/context7-api-check.sh +57 -13
- package/scripts/wave-orchestrator/agent-state.mjs +42 -0
- package/scripts/wave-orchestrator/autonomous.mjs +42 -6
- package/scripts/wave-orchestrator/clarification-triage.mjs +4 -3
- package/scripts/wave-orchestrator/control-cli.mjs +145 -11
- package/scripts/wave-orchestrator/control-plane.mjs +12 -1
- package/scripts/wave-orchestrator/coordination-store.mjs +124 -4
- package/scripts/wave-orchestrator/coordination.mjs +35 -0
- package/scripts/wave-orchestrator/executors.mjs +11 -6
- package/scripts/wave-orchestrator/gate-engine.mjs +5 -5
- package/scripts/wave-orchestrator/install.mjs +2 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +12 -1
- package/scripts/wave-orchestrator/launcher.mjs +236 -0
- package/scripts/wave-orchestrator/ledger.mjs +14 -12
- package/scripts/wave-orchestrator/reducer-snapshot.mjs +8 -6
- package/scripts/wave-orchestrator/retry-engine.mjs +19 -11
- package/scripts/wave-orchestrator/routing-state.mjs +50 -3
- package/scripts/wave-orchestrator/session-supervisor.mjs +119 -10
- package/scripts/wave-orchestrator/shared.mjs +1 -0
- package/scripts/wave-orchestrator/signals.mjs +681 -0
- package/scripts/wave-orchestrator/task-entity.mjs +4 -4
- package/scripts/wave-orchestrator/terminals.mjs +14 -14
- package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
- package/scripts/wave-orchestrator/wave-files.mjs +15 -21
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +72 -5
- 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
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
isSecurityReviewAgent,
|
|
18
18
|
} from "./role-helpers.mjs";
|
|
19
19
|
import {
|
|
20
|
+
coordinationRecordBlocksWave,
|
|
20
21
|
isOpenCoordinationStatus,
|
|
21
|
-
openClarificationLinkedRequests,
|
|
22
22
|
} from "./coordination-store.mjs";
|
|
23
23
|
|
|
24
24
|
export const TASK_TYPES = new Set([
|
|
@@ -647,7 +647,7 @@ export function buildTasksFromCoordinationState(coordinationState, feedbackReque
|
|
|
647
647
|
const tasks = [];
|
|
648
648
|
|
|
649
649
|
for (const record of coordinationState.clarifications || []) {
|
|
650
|
-
if (!
|
|
650
|
+
if (!coordinationRecordBlocksWave(record)) {
|
|
651
651
|
continue;
|
|
652
652
|
}
|
|
653
653
|
const waveNumber = Number.isFinite(record.wave) ? record.wave : 0;
|
|
@@ -683,7 +683,7 @@ export function buildTasksFromCoordinationState(coordinationState, feedbackReque
|
|
|
683
683
|
}
|
|
684
684
|
|
|
685
685
|
for (const record of coordinationState.humanFeedback || []) {
|
|
686
|
-
if (!
|
|
686
|
+
if (!coordinationRecordBlocksWave(record)) {
|
|
687
687
|
continue;
|
|
688
688
|
}
|
|
689
689
|
const waveNumber = Number.isFinite(record.wave) ? record.wave : 0;
|
|
@@ -719,7 +719,7 @@ export function buildTasksFromCoordinationState(coordinationState, feedbackReque
|
|
|
719
719
|
}
|
|
720
720
|
|
|
721
721
|
for (const record of coordinationState.humanEscalations || []) {
|
|
722
|
-
if (!
|
|
722
|
+
if (!coordinationRecordBlocksWave(record)) {
|
|
723
723
|
continue;
|
|
724
724
|
}
|
|
725
725
|
const waveNumber = Number.isFinite(record.wave) ? record.wave : 0;
|
|
@@ -84,6 +84,14 @@ function extractTmuxSessionName(command, socketName) {
|
|
|
84
84
|
return sessionName || null;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function sanitizeTmuxSessionName(value) {
|
|
88
|
+
return String(value || "").replace(/[^a-zA-Z0-9:_-]/g, "_");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createWaveAgentSessionName(lanePaths, wave, agentSlug) {
|
|
92
|
+
return sanitizeTmuxSessionName(`${lanePaths.tmuxSessionPrefix}${wave}_${agentSlug}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
export function createTemporaryTerminalEntries(
|
|
88
96
|
lanePaths,
|
|
89
97
|
wave,
|
|
@@ -93,10 +101,7 @@ export function createTemporaryTerminalEntries(
|
|
|
93
101
|
) {
|
|
94
102
|
const agentEntries = agents.map((agent) => {
|
|
95
103
|
const terminalName = `${lanePaths.terminalNamePrefix}${wave}-${agent.slug}`;
|
|
96
|
-
const sessionName =
|
|
97
|
-
/[^a-zA-Z0-9_-]/g,
|
|
98
|
-
"_",
|
|
99
|
-
);
|
|
104
|
+
const sessionName = createWaveAgentSessionName(lanePaths, wave, agent.slug);
|
|
100
105
|
return {
|
|
101
106
|
terminalName,
|
|
102
107
|
sessionName,
|
|
@@ -111,9 +116,8 @@ export function createTemporaryTerminalEntries(
|
|
|
111
116
|
if (!includeDashboard) {
|
|
112
117
|
return agentEntries;
|
|
113
118
|
}
|
|
114
|
-
const dashboardSessionName =
|
|
115
|
-
|
|
116
|
-
"_",
|
|
119
|
+
const dashboardSessionName = sanitizeTmuxSessionName(
|
|
120
|
+
`${lanePaths.tmuxDashboardSessionPrefix}${wave}`,
|
|
117
121
|
);
|
|
118
122
|
agentEntries.push({
|
|
119
123
|
terminalName: `${lanePaths.dashboardTerminalNamePrefix}${wave}`,
|
|
@@ -129,9 +133,8 @@ export function createTemporaryTerminalEntries(
|
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
export function createGlobalDashboardTerminalEntry(lanePaths, runTag) {
|
|
132
|
-
const sessionName =
|
|
133
|
-
|
|
134
|
-
"_",
|
|
136
|
+
const sessionName = sanitizeTmuxSessionName(
|
|
137
|
+
`${lanePaths.tmuxGlobalDashboardSessionPrefix}_current`,
|
|
135
138
|
);
|
|
136
139
|
return {
|
|
137
140
|
terminalName: lanePaths.globalDashboardTerminalName,
|
|
@@ -146,10 +149,7 @@ export function createGlobalDashboardTerminalEntry(lanePaths, runTag) {
|
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
export function createCurrentWaveDashboardTerminalEntry(lanePaths) {
|
|
149
|
-
const sessionName = `${lanePaths.tmuxDashboardSessionPrefix}_current
|
|
150
|
-
/[^a-zA-Z0-9:_-]/g,
|
|
151
|
-
"_",
|
|
152
|
-
);
|
|
152
|
+
const sessionName = sanitizeTmuxSessionName(`${lanePaths.tmuxDashboardSessionPrefix}_current`);
|
|
153
153
|
const terminalName = currentWaveDashboardTerminalName(lanePaths);
|
|
154
154
|
return {
|
|
155
155
|
terminalName,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from "./shared.mjs";
|
|
37
37
|
import { normalizeContext7Config, hashAgentPromptFingerprint } from "./context7.mjs";
|
|
38
38
|
import {
|
|
39
|
-
|
|
39
|
+
coordinationRecordBlocksWave,
|
|
40
40
|
openClarificationLinkedRequests,
|
|
41
41
|
readMaterializedCoordinationState,
|
|
42
42
|
} from "./coordination-store.mjs";
|
|
@@ -2154,23 +2154,19 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2154
2154
|
? "claude.maxTurns"
|
|
2155
2155
|
: profile?.claude?.maxTurns !== null && profile?.claude?.maxTurns !== undefined
|
|
2156
2156
|
? "claude.maxTurns"
|
|
2157
|
-
:
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
? "claude.maxTurns"
|
|
2162
|
-
: null;
|
|
2157
|
+
: laneProfile.executors.claude.maxTurns !== null &&
|
|
2158
|
+
laneProfile.executors.claude.maxTurns !== undefined
|
|
2159
|
+
? "claude.maxTurns"
|
|
2160
|
+
: null;
|
|
2163
2161
|
const opencodeStepsSource =
|
|
2164
2162
|
executorConfig?.opencode?.steps !== null && executorConfig?.opencode?.steps !== undefined
|
|
2165
2163
|
? "opencode.steps"
|
|
2166
2164
|
: profile?.opencode?.steps !== null && profile?.opencode?.steps !== undefined
|
|
2167
2165
|
? "opencode.steps"
|
|
2168
|
-
:
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
? "opencode.steps"
|
|
2173
|
-
: null;
|
|
2166
|
+
: laneProfile.executors.opencode.steps !== null &&
|
|
2167
|
+
laneProfile.executors.opencode.steps !== undefined
|
|
2168
|
+
? "opencode.steps"
|
|
2169
|
+
: null;
|
|
2174
2170
|
return {
|
|
2175
2171
|
id: executorId,
|
|
2176
2172
|
initialExecutorId: executorId,
|
|
@@ -2253,7 +2249,6 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2253
2249
|
maxTurns:
|
|
2254
2250
|
executorConfig?.claude?.maxTurns ??
|
|
2255
2251
|
profile?.claude?.maxTurns ??
|
|
2256
|
-
runtimeBudget.turns ??
|
|
2257
2252
|
laneProfile.executors.claude.maxTurns,
|
|
2258
2253
|
maxTurnsSource: claudeMaxTurnsSource,
|
|
2259
2254
|
},
|
|
@@ -2271,7 +2266,6 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
2271
2266
|
steps:
|
|
2272
2267
|
executorConfig?.opencode?.steps ??
|
|
2273
2268
|
profile?.opencode?.steps ??
|
|
2274
|
-
runtimeBudget.turns ??
|
|
2275
2269
|
laneProfile.executors.opencode.steps,
|
|
2276
2270
|
stepsSource: opencodeStepsSource,
|
|
2277
2271
|
},
|
|
@@ -3114,7 +3108,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
3114
3108
|
coordinationLogPath,
|
|
3115
3109
|
);
|
|
3116
3110
|
const openClarificationIds = coordinationState.clarifications
|
|
3117
|
-
.filter((record) =>
|
|
3111
|
+
.filter((record) => coordinationRecordBlocksWave(record))
|
|
3118
3112
|
.map((record) => record.id);
|
|
3119
3113
|
if (openClarificationIds.length > 0) {
|
|
3120
3114
|
pushWaveCompletionReason(
|
|
@@ -3123,9 +3117,9 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
3123
3117
|
`Open clarification records: ${openClarificationIds.join(", ")}.`,
|
|
3124
3118
|
);
|
|
3125
3119
|
}
|
|
3126
|
-
const openClarificationRequestIds = openClarificationLinkedRequests(coordinationState)
|
|
3127
|
-
(record) => record
|
|
3128
|
-
|
|
3120
|
+
const openClarificationRequestIds = openClarificationLinkedRequests(coordinationState)
|
|
3121
|
+
.filter((record) => coordinationRecordBlocksWave(record))
|
|
3122
|
+
.map((record) => record.id);
|
|
3129
3123
|
if (openClarificationRequestIds.length > 0) {
|
|
3130
3124
|
pushWaveCompletionReason(
|
|
3131
3125
|
reasons,
|
|
@@ -3134,7 +3128,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
3134
3128
|
);
|
|
3135
3129
|
}
|
|
3136
3130
|
const openHumanEscalationIds = coordinationState.humanEscalations
|
|
3137
|
-
.filter((record) =>
|
|
3131
|
+
.filter((record) => coordinationRecordBlocksWave(record))
|
|
3138
3132
|
.map((record) => record.id);
|
|
3139
3133
|
if (openHumanEscalationIds.length > 0) {
|
|
3140
3134
|
pushWaveCompletionReason(
|
|
@@ -3144,7 +3138,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
3144
3138
|
);
|
|
3145
3139
|
}
|
|
3146
3140
|
const openHumanFeedbackIds = coordinationState.humanFeedback
|
|
3147
|
-
.filter((record) =>
|
|
3141
|
+
.filter((record) => coordinationRecordBlocksWave(record))
|
|
3148
3142
|
.map((record) => record.id);
|
|
3149
3143
|
if (openHumanFeedbackIds.length > 0) {
|
|
3150
3144
|
pushWaveCompletionReason(
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { materializeControlPlaneState } from "./control-plane.mjs";
|
|
2
2
|
import {
|
|
3
3
|
buildCoordinationResponseMetrics,
|
|
4
|
+
coordinationBlockerSeverity,
|
|
5
|
+
coordinationRecordBlocksWave,
|
|
6
|
+
coordinationRecordIsHardBlocker,
|
|
4
7
|
isOpenCoordinationStatus,
|
|
5
8
|
materializeCoordinationState,
|
|
6
9
|
openClarificationLinkedRequests,
|
|
@@ -135,18 +138,18 @@ function derivePhase({
|
|
|
135
138
|
clarificationBarrier,
|
|
136
139
|
}) {
|
|
137
140
|
const blockers = (coordinationState?.blockers || []).filter(
|
|
138
|
-
(record) =>
|
|
139
|
-
isOpenCoordinationStatus(record.status) &&
|
|
140
|
-
["high", "urgent"].includes(record.priority),
|
|
141
|
+
(record) => coordinationRecordIsHardBlocker(record),
|
|
141
142
|
);
|
|
142
143
|
if (blockers.length > 0) {
|
|
143
144
|
return "blocked";
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
const openClarifications = (coordinationState?.clarifications || []).filter(
|
|
147
|
-
(record) =>
|
|
148
|
+
(record) => coordinationRecordBlocksWave(record),
|
|
149
|
+
);
|
|
150
|
+
const openClarificationRequests = openClarificationLinkedRequests(coordinationState).filter(
|
|
151
|
+
(record) => coordinationRecordBlocksWave(record),
|
|
148
152
|
);
|
|
149
|
-
const openClarificationRequests = openClarificationLinkedRequests(coordinationState);
|
|
150
153
|
if (openClarifications.length > 0 || openClarificationRequests.length > 0) {
|
|
151
154
|
return "clarifying";
|
|
152
155
|
}
|
|
@@ -228,6 +231,51 @@ function derivePhase({
|
|
|
228
231
|
return "running";
|
|
229
232
|
}
|
|
230
233
|
|
|
234
|
+
function gateBlockerMetadata(gateName, gate) {
|
|
235
|
+
if (!gate || gate.ok !== false) {
|
|
236
|
+
return {
|
|
237
|
+
blocking: false,
|
|
238
|
+
blockerSeverity: "advisory",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (gateName === "componentGate" && gate.statusCode === "shared-component-sibling-pending") {
|
|
242
|
+
return {
|
|
243
|
+
blocking: true,
|
|
244
|
+
blockerSeverity: "soft",
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (
|
|
248
|
+
["implementationGate", "contEvalGate"].includes(gateName)
|
|
249
|
+
) {
|
|
250
|
+
return {
|
|
251
|
+
blocking: true,
|
|
252
|
+
blockerSeverity: "proof-critical",
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (
|
|
256
|
+
[
|
|
257
|
+
"designGate",
|
|
258
|
+
"integrationBarrier",
|
|
259
|
+
"documentationGate",
|
|
260
|
+
"contQaGate",
|
|
261
|
+
"clarificationBarrier",
|
|
262
|
+
"helperAssignmentBarrier",
|
|
263
|
+
"dependencyBarrier",
|
|
264
|
+
"componentMatrixGate",
|
|
265
|
+
"componentGate",
|
|
266
|
+
].includes(gateName)
|
|
267
|
+
) {
|
|
268
|
+
return {
|
|
269
|
+
blocking: true,
|
|
270
|
+
blockerSeverity: "closure-critical",
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
blocking: true,
|
|
275
|
+
blockerSeverity: "hard",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
231
279
|
/**
|
|
232
280
|
* Map waveState from phase for end-state output.
|
|
233
281
|
*/
|
|
@@ -338,6 +386,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
338
386
|
if (!isOpenCoordinationStatus(record.status)) {
|
|
339
387
|
continue;
|
|
340
388
|
}
|
|
389
|
+
const blocking = coordinationRecordBlocksWave(record);
|
|
390
|
+
const blockerSeverity = coordinationBlockerSeverity(record);
|
|
341
391
|
blockers.push({
|
|
342
392
|
kind: "coordination-blocker",
|
|
343
393
|
id: record.id,
|
|
@@ -348,6 +398,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
348
398
|
? record.targets
|
|
349
399
|
: (record.agentId ? [record.agentId] : []),
|
|
350
400
|
resolutionHint: record.resolutionHint || null,
|
|
401
|
+
blocking,
|
|
402
|
+
blockerSeverity,
|
|
351
403
|
});
|
|
352
404
|
}
|
|
353
405
|
|
|
@@ -355,6 +407,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
355
407
|
if (!isOpenCoordinationStatus(record.status)) {
|
|
356
408
|
continue;
|
|
357
409
|
}
|
|
410
|
+
const blocking = coordinationRecordBlocksWave(record);
|
|
411
|
+
const blockerSeverity = coordinationBlockerSeverity(record);
|
|
358
412
|
blockers.push({
|
|
359
413
|
kind: "clarification",
|
|
360
414
|
id: record.id,
|
|
@@ -365,6 +419,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
365
419
|
? record.targets
|
|
366
420
|
: (record.agentId ? [record.agentId] : []),
|
|
367
421
|
resolutionHint: "Resolve clarification before proceeding.",
|
|
422
|
+
blocking,
|
|
423
|
+
blockerSeverity,
|
|
368
424
|
});
|
|
369
425
|
}
|
|
370
426
|
|
|
@@ -372,6 +428,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
372
428
|
if (!isOpenCoordinationStatus(record.status)) {
|
|
373
429
|
continue;
|
|
374
430
|
}
|
|
431
|
+
const blocking = coordinationRecordBlocksWave(record);
|
|
432
|
+
const blockerSeverity = coordinationBlockerSeverity(record);
|
|
375
433
|
blockers.push({
|
|
376
434
|
kind: "human-escalation",
|
|
377
435
|
id: record.id,
|
|
@@ -382,6 +440,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
382
440
|
? record.targets
|
|
383
441
|
: (record.agentId ? [record.agentId] : []),
|
|
384
442
|
resolutionHint: "Human intervention required.",
|
|
443
|
+
blocking,
|
|
444
|
+
blockerSeverity,
|
|
385
445
|
});
|
|
386
446
|
}
|
|
387
447
|
|
|
@@ -389,6 +449,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
389
449
|
if (!isOpenCoordinationStatus(record.status)) {
|
|
390
450
|
continue;
|
|
391
451
|
}
|
|
452
|
+
const blocking = coordinationRecordBlocksWave(record);
|
|
453
|
+
const blockerSeverity = coordinationBlockerSeverity(record);
|
|
392
454
|
blockers.push({
|
|
393
455
|
kind: "human-feedback",
|
|
394
456
|
id: record.id,
|
|
@@ -399,6 +461,8 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
399
461
|
? record.targets
|
|
400
462
|
: (record.agentId ? [record.agentId] : []),
|
|
401
463
|
resolutionHint: "Awaiting human feedback.",
|
|
464
|
+
blocking,
|
|
465
|
+
blockerSeverity,
|
|
402
466
|
});
|
|
403
467
|
}
|
|
404
468
|
|
|
@@ -407,12 +471,15 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
|
|
|
407
471
|
if (gateName === "overall" || !gate || gate.ok !== false) {
|
|
408
472
|
continue;
|
|
409
473
|
}
|
|
474
|
+
const metadata = gateBlockerMetadata(gateName, gate);
|
|
410
475
|
blockers.push({
|
|
411
476
|
kind: "gate-failure",
|
|
412
477
|
id: gateName,
|
|
413
478
|
detail: gate.detail || gate.statusCode || "",
|
|
414
479
|
blockedAgentIds: gate.agentId ? [gate.agentId] : [],
|
|
415
480
|
resolutionHint: `Gate ${gateName} must pass before wave closure.`,
|
|
481
|
+
blocking: metadata.blocking,
|
|
482
|
+
blockerSeverity: metadata.blockerSeverity,
|
|
416
483
|
});
|
|
417
484
|
}
|
|
418
485
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
lane="main"
|
|
5
|
+
wave=""
|
|
6
|
+
agent=""
|
|
7
|
+
run_id=""
|
|
8
|
+
dry_run="0"
|
|
9
|
+
json_output="0"
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
cat <<'EOF'
|
|
13
|
+
Usage:
|
|
14
|
+
scripts/wave-status.sh [--lane <lane>] [--wave <n>] [--agent <id>] [--run <id>] [--dry-run] [--json]
|
|
15
|
+
|
|
16
|
+
Exit codes:
|
|
17
|
+
0 completed
|
|
18
|
+
10 waiting or running
|
|
19
|
+
20 input required
|
|
20
|
+
40 failed
|
|
21
|
+
EOF
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
while [ "$#" -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--lane)
|
|
27
|
+
lane="${2:-}"
|
|
28
|
+
shift 2
|
|
29
|
+
;;
|
|
30
|
+
--wave)
|
|
31
|
+
wave="${2:-}"
|
|
32
|
+
shift 2
|
|
33
|
+
;;
|
|
34
|
+
--agent)
|
|
35
|
+
agent="${2:-}"
|
|
36
|
+
shift 2
|
|
37
|
+
;;
|
|
38
|
+
--run)
|
|
39
|
+
run_id="${2:-}"
|
|
40
|
+
shift 2
|
|
41
|
+
;;
|
|
42
|
+
--dry-run)
|
|
43
|
+
dry_run="1"
|
|
44
|
+
shift
|
|
45
|
+
;;
|
|
46
|
+
--json)
|
|
47
|
+
json_output="1"
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
--help|-h)
|
|
51
|
+
usage
|
|
52
|
+
exit 0
|
|
53
|
+
;;
|
|
54
|
+
*)
|
|
55
|
+
echo "Unknown argument: $1" >&2
|
|
56
|
+
usage >&2
|
|
57
|
+
exit 2
|
|
58
|
+
;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
run_wave_cli() {
|
|
63
|
+
if [ -n "${WAVE_WRAPPER_ENTRY:-}" ]; then
|
|
64
|
+
node "$WAVE_WRAPPER_ENTRY" "$@"
|
|
65
|
+
return
|
|
66
|
+
fi
|
|
67
|
+
if [ -f "scripts/wave.mjs" ]; then
|
|
68
|
+
node "scripts/wave.mjs" "$@"
|
|
69
|
+
return
|
|
70
|
+
fi
|
|
71
|
+
if [ -f "node_modules/@chllming/wave-orchestration/scripts/wave.mjs" ]; then
|
|
72
|
+
node "node_modules/@chllming/wave-orchestration/scripts/wave.mjs" "$@"
|
|
73
|
+
return
|
|
74
|
+
fi
|
|
75
|
+
if command -v pnpm >/dev/null 2>&1; then
|
|
76
|
+
pnpm exec wave "$@"
|
|
77
|
+
return
|
|
78
|
+
fi
|
|
79
|
+
echo "Unable to locate Wave CLI. Set WAVE_WRAPPER_ENTRY or install the package locally." >&2
|
|
80
|
+
exit 2
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
infer_wave() {
|
|
84
|
+
node - "$lane" "$run_id" "$dry_run" <<'NODE'
|
|
85
|
+
const fs = require("node:fs");
|
|
86
|
+
const path = require("node:path");
|
|
87
|
+
|
|
88
|
+
const lane = process.argv[2] || "main";
|
|
89
|
+
const runId = process.argv[3] || "";
|
|
90
|
+
const dryRun = process.argv[4] === "1";
|
|
91
|
+
const stateDir = runId
|
|
92
|
+
? path.join(process.cwd(), ".tmp", `${lane}-wave-launcher`, "adhoc", runId, dryRun ? "dry-run" : "")
|
|
93
|
+
: path.join(process.cwd(), ".tmp", `${lane}-wave-launcher`, dryRun ? "dry-run" : "");
|
|
94
|
+
const runStatePath = path.join(stateDir, "run-state.json");
|
|
95
|
+
let payload = null;
|
|
96
|
+
try {
|
|
97
|
+
payload = JSON.parse(fs.readFileSync(runStatePath, "utf8"));
|
|
98
|
+
} catch {
|
|
99
|
+
payload = null;
|
|
100
|
+
}
|
|
101
|
+
const waves = Object.values(payload?.waves || {})
|
|
102
|
+
.filter((entry) => entry && typeof entry === "object")
|
|
103
|
+
.map((entry) => ({
|
|
104
|
+
wave: Number.parseInt(String(entry.wave ?? ""), 10),
|
|
105
|
+
state: String(entry.currentState || "").trim().toLowerCase(),
|
|
106
|
+
}))
|
|
107
|
+
.filter((entry) => Number.isFinite(entry.wave))
|
|
108
|
+
.sort((left, right) => left.wave - right.wave);
|
|
109
|
+
const active = waves.findLast(
|
|
110
|
+
(entry) => !["completed", "failed", "timed_out", "timed-out"].includes(entry.state),
|
|
111
|
+
);
|
|
112
|
+
if (active) {
|
|
113
|
+
process.stdout.write(String(active.wave));
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
const completed = Array.isArray(payload?.completedWaves)
|
|
117
|
+
? payload.completedWaves
|
|
118
|
+
.map((value) => Number.parseInt(String(value), 10))
|
|
119
|
+
.filter((value) => Number.isFinite(value))
|
|
120
|
+
.sort((left, right) => left - right)
|
|
121
|
+
: [];
|
|
122
|
+
process.stdout.write(String(completed.at(-1) ?? 0));
|
|
123
|
+
NODE
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if [ -z "$wave" ]; then
|
|
127
|
+
wave="$(infer_wave)"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
status_args=(control status --lane "$lane" --wave "$wave" --json)
|
|
131
|
+
if [ -n "$agent" ]; then
|
|
132
|
+
status_args+=(--agent "$agent")
|
|
133
|
+
fi
|
|
134
|
+
if [ -n "$run_id" ]; then
|
|
135
|
+
status_args+=(--run "$run_id")
|
|
136
|
+
fi
|
|
137
|
+
if [ "$dry_run" = "1" ]; then
|
|
138
|
+
status_args+=(--dry-run)
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
payload="$(run_wave_cli "${status_args[@]}")"
|
|
142
|
+
|
|
143
|
+
if [ "$json_output" = "1" ]; then
|
|
144
|
+
printf '%s\n' "$payload"
|
|
145
|
+
exit 0
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
PAYLOAD="$payload" node - "$lane" "$wave" "$agent" <<'NODE'
|
|
149
|
+
const fs = require("node:fs");
|
|
150
|
+
|
|
151
|
+
const lane = process.argv[2] || "main";
|
|
152
|
+
const wave = Number.parseInt(String(process.argv[3] || "0"), 10) || 0;
|
|
153
|
+
const agentId = String(process.argv[4] || "").trim();
|
|
154
|
+
const payload = JSON.parse(process.env.PAYLOAD || "{}");
|
|
155
|
+
const signals = payload?.signals || {};
|
|
156
|
+
const snapshot = agentId
|
|
157
|
+
? (Array.isArray(signals.agents) ? signals.agents.find((entry) => entry.agentId === agentId) : null)
|
|
158
|
+
: signals.wave;
|
|
159
|
+
const effective = snapshot || {
|
|
160
|
+
signal: payload?.blockingEdge?.kind === "human-input" ? "feedback-requested" : "waiting",
|
|
161
|
+
lane,
|
|
162
|
+
wave,
|
|
163
|
+
phase: payload?.phase || "unknown",
|
|
164
|
+
status: payload?.blockingEdge ? "blocked" : "running",
|
|
165
|
+
blocking: payload?.blockingEdge || null,
|
|
166
|
+
attempt: payload?.activeAttempt?.attemptNumber || 0,
|
|
167
|
+
targetAgentIds: agentId ? [agentId] : [],
|
|
168
|
+
shouldWake: agentId ? true : null,
|
|
169
|
+
version: 0,
|
|
170
|
+
};
|
|
171
|
+
const targetKey = agentId ? "agent" : "agents";
|
|
172
|
+
const targetValue = agentId || (effective.targetAgentIds || []).join(",") || "none";
|
|
173
|
+
const blocking = effective?.blocking?.kind || "none";
|
|
174
|
+
const shouldWake =
|
|
175
|
+
typeof effective.shouldWake === "boolean" ? (effective.shouldWake ? "yes" : "no") : "n/a";
|
|
176
|
+
console.log(
|
|
177
|
+
[
|
|
178
|
+
`signal=${effective.signal || "waiting"}`,
|
|
179
|
+
`lane=${lane}`,
|
|
180
|
+
`wave=${wave}`,
|
|
181
|
+
`phase=${effective.phase || "unknown"}`,
|
|
182
|
+
`status=${effective.status || "running"}`,
|
|
183
|
+
`blocking=${blocking}`,
|
|
184
|
+
`attempt=${effective.attempt || 0}`,
|
|
185
|
+
`${targetKey}=${targetValue}`,
|
|
186
|
+
`version=${effective.version || 0}`,
|
|
187
|
+
`should_wake=${shouldWake}`,
|
|
188
|
+
].join(" "),
|
|
189
|
+
);
|
|
190
|
+
if (String(effective.signal || "").trim().toLowerCase() === "completed") {
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
if (String(effective.signal || "").trim().toLowerCase() === "failed") {
|
|
194
|
+
process.exit(40);
|
|
195
|
+
}
|
|
196
|
+
if (String(effective.signal || "").trim().toLowerCase() === "feedback-requested") {
|
|
197
|
+
process.exit(20);
|
|
198
|
+
}
|
|
199
|
+
process.exit(10);
|
|
200
|
+
NODE
|