@chllming/wave-orchestration 0.5.1
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 +41 -0
- package/README.md +549 -0
- package/docs/agents/wave-deploy-verifier-role.md +34 -0
- package/docs/agents/wave-documentation-role.md +30 -0
- package/docs/agents/wave-evaluator-role.md +43 -0
- package/docs/agents/wave-infra-role.md +34 -0
- package/docs/agents/wave-integration-role.md +32 -0
- package/docs/agents/wave-launcher-role.md +37 -0
- package/docs/context7/bundles.json +91 -0
- package/docs/plans/component-cutover-matrix.json +112 -0
- package/docs/plans/component-cutover-matrix.md +49 -0
- package/docs/plans/context7-wave-orchestrator.md +130 -0
- package/docs/plans/current-state.md +44 -0
- package/docs/plans/master-plan.md +16 -0
- package/docs/plans/migration.md +23 -0
- package/docs/plans/wave-orchestrator.md +254 -0
- package/docs/plans/waves/wave-0.md +165 -0
- package/docs/reference/github-packages-setup.md +52 -0
- package/docs/reference/migration-0.2-to-0.5.md +622 -0
- package/docs/reference/npmjs-trusted-publishing.md +55 -0
- package/docs/reference/repository-guidance.md +18 -0
- package/docs/reference/runtime-config/README.md +85 -0
- package/docs/reference/runtime-config/claude.md +105 -0
- package/docs/reference/runtime-config/codex.md +81 -0
- package/docs/reference/runtime-config/opencode.md +93 -0
- package/docs/research/agent-context-sources.md +57 -0
- package/docs/roadmap.md +626 -0
- package/package.json +53 -0
- package/releases/manifest.json +101 -0
- package/scripts/context7-api-check.sh +21 -0
- package/scripts/context7-export-env.sh +52 -0
- package/scripts/research/agent-context-archive.mjs +472 -0
- package/scripts/research/generate-agent-context-indexes.mjs +85 -0
- package/scripts/research/import-agent-context-archive.mjs +793 -0
- package/scripts/research/manifests/harness-and-blackboard-2026-03-21.mjs +201 -0
- package/scripts/wave-autonomous.mjs +13 -0
- package/scripts/wave-cli-bootstrap.mjs +27 -0
- package/scripts/wave-dashboard.mjs +11 -0
- package/scripts/wave-human-feedback.mjs +11 -0
- package/scripts/wave-launcher.mjs +11 -0
- package/scripts/wave-local-executor.mjs +13 -0
- package/scripts/wave-orchestrator/agent-state.mjs +416 -0
- package/scripts/wave-orchestrator/autonomous.mjs +367 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +605 -0
- package/scripts/wave-orchestrator/config.mjs +848 -0
- package/scripts/wave-orchestrator/context7.mjs +464 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +286 -0
- package/scripts/wave-orchestrator/coordination-store.mjs +987 -0
- package/scripts/wave-orchestrator/coordination.mjs +768 -0
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +254 -0
- package/scripts/wave-orchestrator/dashboard-state.mjs +473 -0
- package/scripts/wave-orchestrator/dep-cli.mjs +219 -0
- package/scripts/wave-orchestrator/docs-queue.mjs +75 -0
- package/scripts/wave-orchestrator/executors.mjs +385 -0
- package/scripts/wave-orchestrator/feedback.mjs +372 -0
- package/scripts/wave-orchestrator/install.mjs +540 -0
- package/scripts/wave-orchestrator/launcher.mjs +3879 -0
- package/scripts/wave-orchestrator/ledger.mjs +332 -0
- package/scripts/wave-orchestrator/local-executor.mjs +263 -0
- package/scripts/wave-orchestrator/replay.mjs +246 -0
- package/scripts/wave-orchestrator/roots.mjs +10 -0
- package/scripts/wave-orchestrator/routing-state.mjs +542 -0
- package/scripts/wave-orchestrator/shared.mjs +405 -0
- package/scripts/wave-orchestrator/terminals.mjs +209 -0
- package/scripts/wave-orchestrator/traces.mjs +1094 -0
- package/scripts/wave-orchestrator/wave-files.mjs +1923 -0
- package/scripts/wave.mjs +103 -0
- package/wave.config.json +115 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
appendCoordinationRecord,
|
|
5
|
+
clarificationClosureCondition,
|
|
6
|
+
clarificationLinkedRequests,
|
|
7
|
+
isOpenCoordinationStatus,
|
|
8
|
+
readMaterializedCoordinationState,
|
|
9
|
+
} from "./coordination-store.mjs";
|
|
10
|
+
import { createFeedbackRequest } from "./feedback.mjs";
|
|
11
|
+
import { ensureDirectory, writeTextAtomic } from "./shared.mjs";
|
|
12
|
+
|
|
13
|
+
const MAX_ROUTED_CLARIFICATION_CYCLES = 2;
|
|
14
|
+
|
|
15
|
+
function triageLogPath(lanePaths, waveNumber) {
|
|
16
|
+
return path.join(lanePaths.feedbackTriageDir, `wave-${waveNumber}.jsonl`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function pendingHumanPath(lanePaths, waveNumber) {
|
|
20
|
+
return path.join(lanePaths.feedbackTriageDir, `wave-${waveNumber}`, "pending-human.md");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeText(value) {
|
|
24
|
+
return String(value || "").trim();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function lowerText(value) {
|
|
28
|
+
return normalizeText(value).toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function routeRequestId(clarificationId, cycle) {
|
|
32
|
+
return `route-${clarificationId}-${cycle}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function routeCycleForRecord(recordId) {
|
|
36
|
+
const match = String(recordId || "").match(/-(\d+)$/);
|
|
37
|
+
return match ? Number.parseInt(match[1], 10) : 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function appendTriageRecord(filePath, payload) {
|
|
41
|
+
return appendCoordinationRecord(filePath, payload);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function writePendingHumanSummary(filePath, triageState) {
|
|
45
|
+
const openEscalations = (triageState?.humanEscalations || []).filter((record) =>
|
|
46
|
+
isOpenCoordinationStatus(record.status),
|
|
47
|
+
);
|
|
48
|
+
ensureDirectory(path.dirname(filePath));
|
|
49
|
+
const text = [
|
|
50
|
+
"# Pending Human Escalations",
|
|
51
|
+
"",
|
|
52
|
+
...(openEscalations.length > 0
|
|
53
|
+
? openEscalations.map(
|
|
54
|
+
(record) =>
|
|
55
|
+
`- ${record.id}: ${record.summary}${record.artifactRefs?.length ? ` [tickets: ${record.artifactRefs.join(", ")}]` : ""}`,
|
|
56
|
+
)
|
|
57
|
+
: ["- None."]),
|
|
58
|
+
"",
|
|
59
|
+
].join("\n");
|
|
60
|
+
writeTextAtomic(filePath, text);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function artifactRefSet(record) {
|
|
64
|
+
return new Set((record?.artifactRefs || []).map((value) => lowerText(value)));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function recordMatchesArtifactRefs(record, candidates) {
|
|
68
|
+
const refs = artifactRefSet(record);
|
|
69
|
+
if (refs.size === 0) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return candidates.some((candidate) => refs.has(lowerText(candidate)));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function textMentionsAny(text, values) {
|
|
76
|
+
const haystack = lowerText(text);
|
|
77
|
+
return values.some((value) => haystack.includes(lowerText(value)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function findOwnedPathOwners(wave, text) {
|
|
81
|
+
return wave.agents.filter((agent) =>
|
|
82
|
+
Array.isArray(agent.ownedPaths) &&
|
|
83
|
+
agent.ownedPaths.some((ownedPath) => lowerText(text).includes(lowerText(ownedPath))),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function findComponentOwners(wave, text) {
|
|
88
|
+
return wave.agents.filter((agent) =>
|
|
89
|
+
Array.isArray(agent.components) &&
|
|
90
|
+
agent.components.some((componentId) => lowerText(text).includes(lowerText(componentId))),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function findDecisionResolution(record, resolutionContext = {}) {
|
|
95
|
+
const searchableText = [
|
|
96
|
+
record.summary,
|
|
97
|
+
record.detail,
|
|
98
|
+
...(record.artifactRefs || []),
|
|
99
|
+
].join("\n");
|
|
100
|
+
const matchingDecision = (resolutionContext.coordinationState?.decisions || []).find((decision) => {
|
|
101
|
+
if (recordMatchesArtifactRefs(record, decision.artifactRefs || [])) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return textMentionsAny(searchableText, [
|
|
105
|
+
decision.summary,
|
|
106
|
+
decision.detail,
|
|
107
|
+
...(decision.artifactRefs || []),
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
if (!matchingDecision) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
type: "policy",
|
|
115
|
+
guidance:
|
|
116
|
+
matchingDecision.detail ||
|
|
117
|
+
matchingDecision.summary ||
|
|
118
|
+
`Resolved from coordination decision ${matchingDecision.id}.`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function findDocsQueueResolution(record, lanePaths, resolutionContext = {}) {
|
|
123
|
+
const searchableText = [
|
|
124
|
+
record.summary,
|
|
125
|
+
record.detail,
|
|
126
|
+
...(record.artifactRefs || []),
|
|
127
|
+
].join("\n");
|
|
128
|
+
const matchingItem = (resolutionContext.docsQueue?.items || []).find((item) => {
|
|
129
|
+
if (recordMatchesArtifactRefs(record, [item.path, item.id])) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return textMentionsAny(searchableText, [item.id, item.path, item.summary]);
|
|
133
|
+
});
|
|
134
|
+
if (!matchingItem) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
type: "route",
|
|
139
|
+
routeAgentId: lanePaths.documentationAgentId,
|
|
140
|
+
guidance:
|
|
141
|
+
matchingItem.summary ||
|
|
142
|
+
`Documentation reconciliation item ${matchingItem.id} is owned by ${lanePaths.documentationAgentId}.`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function findIntegrationResolution(record, lanePaths, resolutionContext = {}) {
|
|
147
|
+
const searchableText = [
|
|
148
|
+
record.summary,
|
|
149
|
+
record.detail,
|
|
150
|
+
...(record.artifactRefs || []),
|
|
151
|
+
].join("\n");
|
|
152
|
+
const integrationSummary = resolutionContext.integrationSummary || null;
|
|
153
|
+
if (!integrationSummary) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const mentionsIntegration = textMentionsAny(searchableText, [
|
|
157
|
+
...(integrationSummary.openClaims || []),
|
|
158
|
+
"contradict",
|
|
159
|
+
"cross-component",
|
|
160
|
+
"cross component",
|
|
161
|
+
"interface",
|
|
162
|
+
"integration",
|
|
163
|
+
...(integrationSummary.conflictingClaims || []),
|
|
164
|
+
...(integrationSummary.unresolvedBlockers || []),
|
|
165
|
+
...(integrationSummary.changedInterfaces || []),
|
|
166
|
+
...(integrationSummary.crossComponentImpacts || []),
|
|
167
|
+
...(integrationSummary.proofGaps || []),
|
|
168
|
+
...(integrationSummary.deployRisks || []),
|
|
169
|
+
...(integrationSummary.docGaps || []),
|
|
170
|
+
]);
|
|
171
|
+
if (!mentionsIntegration) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
type: "route",
|
|
176
|
+
routeAgentId: lanePaths.integrationAgentId,
|
|
177
|
+
guidance:
|
|
178
|
+
integrationSummary.detail ||
|
|
179
|
+
`Integration state is owned by ${lanePaths.integrationAgentId}.`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function findSummaryOwnerResolution(record, wave, resolutionContext = {}) {
|
|
184
|
+
const searchableText = [
|
|
185
|
+
record.summary,
|
|
186
|
+
record.detail,
|
|
187
|
+
...(record.artifactRefs || []),
|
|
188
|
+
].join("\n");
|
|
189
|
+
for (const agent of wave.agents) {
|
|
190
|
+
const summary = resolutionContext.summariesByAgentId?.[agent.agentId];
|
|
191
|
+
if (!summary) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const matchedDocPath =
|
|
195
|
+
summary.docDelta?.paths?.some((docPath) => textMentionsAny(searchableText, [docPath])) ||
|
|
196
|
+
false;
|
|
197
|
+
const matchedComponent =
|
|
198
|
+
summary.components?.some((component) =>
|
|
199
|
+
textMentionsAny(searchableText, [component.componentId]),
|
|
200
|
+
) || false;
|
|
201
|
+
if (matchedDocPath || matchedComponent) {
|
|
202
|
+
return {
|
|
203
|
+
type: "route",
|
|
204
|
+
routeAgentId: agent.agentId,
|
|
205
|
+
guidance: `Latest summary evidence points to ${agent.agentId} for this clarification.`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function buildPolicyResolution(record, wave, lanePaths, resolutionContext = {}) {
|
|
213
|
+
const combinedText = [record.summary, record.detail, ...(record.artifactRefs || [])].join("\n");
|
|
214
|
+
const lower = lowerText(combinedText);
|
|
215
|
+
const decisionResolution = findDecisionResolution(record, resolutionContext);
|
|
216
|
+
if (decisionResolution) {
|
|
217
|
+
return decisionResolution;
|
|
218
|
+
}
|
|
219
|
+
const docsQueueResolution = findDocsQueueResolution(record, lanePaths, resolutionContext);
|
|
220
|
+
if (docsQueueResolution) {
|
|
221
|
+
return docsQueueResolution;
|
|
222
|
+
}
|
|
223
|
+
const pathOwners = findOwnedPathOwners(wave, combinedText).filter(
|
|
224
|
+
(agent) => agent.agentId !== record.agentId,
|
|
225
|
+
);
|
|
226
|
+
if (pathOwners.length > 0) {
|
|
227
|
+
return {
|
|
228
|
+
type: "route",
|
|
229
|
+
routeAgentId: pathOwners[0].agentId,
|
|
230
|
+
guidance: `Ownership policy resolved this clarification to ${pathOwners[0].agentId}.`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const componentOwners = findComponentOwners(wave, combinedText).filter(
|
|
234
|
+
(agent) => agent.agentId !== record.agentId,
|
|
235
|
+
);
|
|
236
|
+
if (componentOwners.length > 0) {
|
|
237
|
+
return {
|
|
238
|
+
type: "route",
|
|
239
|
+
routeAgentId: componentOwners[0].agentId,
|
|
240
|
+
guidance: `Component ownership resolved this clarification to ${componentOwners[0].agentId}.`,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const summaryOwnerResolution = findSummaryOwnerResolution(record, wave, resolutionContext);
|
|
244
|
+
if (summaryOwnerResolution) {
|
|
245
|
+
return summaryOwnerResolution;
|
|
246
|
+
}
|
|
247
|
+
const integrationResolution = findIntegrationResolution(record, lanePaths, resolutionContext);
|
|
248
|
+
if (integrationResolution) {
|
|
249
|
+
return integrationResolution;
|
|
250
|
+
}
|
|
251
|
+
if (
|
|
252
|
+
lower.includes("shared-plan") ||
|
|
253
|
+
lower.includes("shared plan") ||
|
|
254
|
+
lower.includes("docs/plans/") ||
|
|
255
|
+
lower.includes("component-cutover-matrix")
|
|
256
|
+
) {
|
|
257
|
+
return {
|
|
258
|
+
type: "route",
|
|
259
|
+
routeAgentId: lanePaths.documentationAgentId,
|
|
260
|
+
guidance: `Shared plan and component matrix updates are owned by ${lanePaths.documentationAgentId}.`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (lower.includes("evaluator") || lower.includes("gate")) {
|
|
264
|
+
return {
|
|
265
|
+
type: "route",
|
|
266
|
+
routeAgentId: lanePaths.evaluatorAgentId,
|
|
267
|
+
guidance: `Final pass/fail judgement and gate interpretation are owned by ${lanePaths.evaluatorAgentId}.`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function updateClarificationRecord(coordinationLogPath, record, status, detail, attempt) {
|
|
274
|
+
appendCoordinationRecord(coordinationLogPath, {
|
|
275
|
+
...record,
|
|
276
|
+
status,
|
|
277
|
+
detail,
|
|
278
|
+
attempt,
|
|
279
|
+
updatedAt: undefined,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function resolvedEscalationForClarification(coordinationState, clarificationId) {
|
|
284
|
+
return (coordinationState?.humanEscalations || []).find(
|
|
285
|
+
(record) =>
|
|
286
|
+
record.closureCondition === clarificationClosureCondition(clarificationId) &&
|
|
287
|
+
["resolved", "closed"].includes(record.status),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function openEscalationForClarification(coordinationState, clarificationId) {
|
|
292
|
+
return (coordinationState?.humanEscalations || []).find(
|
|
293
|
+
(record) =>
|
|
294
|
+
record.closureCondition === clarificationClosureCondition(clarificationId) &&
|
|
295
|
+
isOpenCoordinationStatus(record.status),
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function latestRouteAttempt(requests) {
|
|
300
|
+
return requests.reduce(
|
|
301
|
+
(maxAttempt, record) =>
|
|
302
|
+
Math.max(maxAttempt, Number.parseInt(String(record.attempt || 0), 10) || 0),
|
|
303
|
+
0,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function supersedeOpenRequests(coordinationLogPath, requests, attempt) {
|
|
308
|
+
for (const request of requests) {
|
|
309
|
+
if (!isOpenCoordinationStatus(request.status)) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
appendCoordinationRecord(coordinationLogPath, {
|
|
313
|
+
...request,
|
|
314
|
+
status: "superseded",
|
|
315
|
+
detail: `Superseded by clarification reroute at attempt ${attempt}.`,
|
|
316
|
+
attempt,
|
|
317
|
+
updatedAt: undefined,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function createClarificationRoute({
|
|
323
|
+
triagePath,
|
|
324
|
+
coordinationLogPath,
|
|
325
|
+
lanePaths,
|
|
326
|
+
wave,
|
|
327
|
+
record,
|
|
328
|
+
routeAgentId,
|
|
329
|
+
guidance,
|
|
330
|
+
cycle,
|
|
331
|
+
attempt,
|
|
332
|
+
}) {
|
|
333
|
+
const routeId = routeRequestId(record.id, cycle);
|
|
334
|
+
const closureCondition = clarificationClosureCondition(record.id);
|
|
335
|
+
updateClarificationRecord(coordinationLogPath, record, "in_progress", guidance, attempt);
|
|
336
|
+
appendTriageRecord(triagePath, {
|
|
337
|
+
id: `triage-${record.id}-route-${cycle}`,
|
|
338
|
+
lane: lanePaths.lane,
|
|
339
|
+
wave: wave.wave,
|
|
340
|
+
agentId: "launcher",
|
|
341
|
+
kind: "orchestrator-guidance",
|
|
342
|
+
targets: [`agent:${record.agentId}`],
|
|
343
|
+
dependsOn: [record.id],
|
|
344
|
+
priority: record.priority || "high",
|
|
345
|
+
summary: `Clarification ${record.id} routed to ${routeAgentId}`,
|
|
346
|
+
detail: guidance,
|
|
347
|
+
status: "resolved",
|
|
348
|
+
attempt,
|
|
349
|
+
source: "launcher",
|
|
350
|
+
});
|
|
351
|
+
appendTriageRecord(triagePath, {
|
|
352
|
+
id: routeId,
|
|
353
|
+
lane: lanePaths.lane,
|
|
354
|
+
wave: wave.wave,
|
|
355
|
+
agentId: "launcher",
|
|
356
|
+
kind: "request",
|
|
357
|
+
targets: [`agent:${routeAgentId}`],
|
|
358
|
+
dependsOn: [record.id],
|
|
359
|
+
closureCondition,
|
|
360
|
+
priority: record.priority || "high",
|
|
361
|
+
summary: `Clarification follow-up for ${record.agentId}`,
|
|
362
|
+
detail: `${record.summary}\n\n${guidance}`,
|
|
363
|
+
artifactRefs: record.artifactRefs || [],
|
|
364
|
+
status: "open",
|
|
365
|
+
attempt,
|
|
366
|
+
source: "launcher",
|
|
367
|
+
});
|
|
368
|
+
appendCoordinationRecord(coordinationLogPath, {
|
|
369
|
+
id: routeId,
|
|
370
|
+
lane: lanePaths.lane,
|
|
371
|
+
wave: wave.wave,
|
|
372
|
+
agentId: "launcher",
|
|
373
|
+
kind: "request",
|
|
374
|
+
targets: [`agent:${routeAgentId}`],
|
|
375
|
+
dependsOn: [record.id],
|
|
376
|
+
closureCondition,
|
|
377
|
+
priority: record.priority || "high",
|
|
378
|
+
summary: `Clarification follow-up for ${record.agentId}`,
|
|
379
|
+
detail: `${record.summary}\n\n${guidance}`,
|
|
380
|
+
artifactRefs: record.artifactRefs || [],
|
|
381
|
+
status: "open",
|
|
382
|
+
attempt,
|
|
383
|
+
source: "launcher",
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function escalateClarificationToHuman({
|
|
388
|
+
triagePath,
|
|
389
|
+
lanePaths,
|
|
390
|
+
wave,
|
|
391
|
+
record,
|
|
392
|
+
orchestratorId,
|
|
393
|
+
attempt,
|
|
394
|
+
reason,
|
|
395
|
+
}) {
|
|
396
|
+
const humanRequest = createFeedbackRequest({
|
|
397
|
+
feedbackStateDir: lanePaths.feedbackStateDir,
|
|
398
|
+
feedbackRequestsDir: lanePaths.feedbackRequestsDir,
|
|
399
|
+
lane: lanePaths.lane,
|
|
400
|
+
wave: wave.wave,
|
|
401
|
+
agentId: record.agentId,
|
|
402
|
+
orchestratorId,
|
|
403
|
+
question: record.summary || "Clarification requested",
|
|
404
|
+
context: record.detail || "",
|
|
405
|
+
});
|
|
406
|
+
const escalationId = `escalation-${humanRequest.requestId}`;
|
|
407
|
+
const escalationRecord = {
|
|
408
|
+
id: escalationId,
|
|
409
|
+
lane: lanePaths.lane,
|
|
410
|
+
wave: wave.wave,
|
|
411
|
+
agentId: "launcher",
|
|
412
|
+
kind: "human-escalation",
|
|
413
|
+
targets: [`agent:${record.agentId}`],
|
|
414
|
+
dependsOn: [record.id],
|
|
415
|
+
closureCondition: clarificationClosureCondition(record.id),
|
|
416
|
+
priority: record.priority || "high",
|
|
417
|
+
summary: record.summary || "Human escalation required",
|
|
418
|
+
detail: reason || record.detail || "",
|
|
419
|
+
artifactRefs: [humanRequest.requestId],
|
|
420
|
+
status: "open",
|
|
421
|
+
attempt,
|
|
422
|
+
source: "launcher",
|
|
423
|
+
};
|
|
424
|
+
appendTriageRecord(triagePath, escalationRecord);
|
|
425
|
+
return escalationRecord;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function triageClarificationRequests({
|
|
429
|
+
lanePaths,
|
|
430
|
+
wave,
|
|
431
|
+
coordinationLogPath,
|
|
432
|
+
coordinationState,
|
|
433
|
+
orchestratorId,
|
|
434
|
+
attempt = 0,
|
|
435
|
+
resolutionContext = {},
|
|
436
|
+
}) {
|
|
437
|
+
ensureDirectory(lanePaths.feedbackTriageDir);
|
|
438
|
+
const triagePath = triageLogPath(lanePaths, wave.wave);
|
|
439
|
+
const openClarifications = (coordinationState?.clarifications || []).filter((record) =>
|
|
440
|
+
isOpenCoordinationStatus(record.status),
|
|
441
|
+
);
|
|
442
|
+
let changed = false;
|
|
443
|
+
|
|
444
|
+
for (const record of openClarifications) {
|
|
445
|
+
const linkedRequests = clarificationLinkedRequests(coordinationState, record.id);
|
|
446
|
+
const openLinkedRequests = linkedRequests.filter((entry) =>
|
|
447
|
+
isOpenCoordinationStatus(entry.status),
|
|
448
|
+
);
|
|
449
|
+
const resolvedLinkedRequest = linkedRequests.find((entry) =>
|
|
450
|
+
["resolved", "closed"].includes(entry.status),
|
|
451
|
+
);
|
|
452
|
+
const resolvedEscalation = resolvedEscalationForClarification(coordinationState, record.id);
|
|
453
|
+
if (resolvedLinkedRequest || resolvedEscalation) {
|
|
454
|
+
updateClarificationRecord(
|
|
455
|
+
coordinationLogPath,
|
|
456
|
+
record,
|
|
457
|
+
"resolved",
|
|
458
|
+
resolvedLinkedRequest
|
|
459
|
+
? `Resolved via ${resolvedLinkedRequest.id}.`
|
|
460
|
+
: `Resolved via ${resolvedEscalation.id}.`,
|
|
461
|
+
attempt,
|
|
462
|
+
);
|
|
463
|
+
changed = true;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const resolution = buildPolicyResolution(record, wave, lanePaths, {
|
|
468
|
+
...resolutionContext,
|
|
469
|
+
coordinationState,
|
|
470
|
+
});
|
|
471
|
+
if (resolution?.type === "policy") {
|
|
472
|
+
updateClarificationRecord(coordinationLogPath, record, "resolved", resolution.guidance, attempt);
|
|
473
|
+
appendTriageRecord(triagePath, {
|
|
474
|
+
id: `triage-${record.id}-policy`,
|
|
475
|
+
lane: lanePaths.lane,
|
|
476
|
+
wave: wave.wave,
|
|
477
|
+
agentId: "launcher",
|
|
478
|
+
kind: "resolved-by-policy",
|
|
479
|
+
targets: [`agent:${record.agentId}`],
|
|
480
|
+
dependsOn: [record.id],
|
|
481
|
+
priority: record.priority || "high",
|
|
482
|
+
summary: `Clarification ${record.id} resolved from repo state`,
|
|
483
|
+
detail: resolution.guidance,
|
|
484
|
+
status: "resolved",
|
|
485
|
+
attempt,
|
|
486
|
+
source: "launcher",
|
|
487
|
+
});
|
|
488
|
+
appendCoordinationRecord(coordinationLogPath, {
|
|
489
|
+
id: `triage-${record.id}-policy`,
|
|
490
|
+
lane: lanePaths.lane,
|
|
491
|
+
wave: wave.wave,
|
|
492
|
+
agentId: "launcher",
|
|
493
|
+
kind: "resolved-by-policy",
|
|
494
|
+
targets: [`agent:${record.agentId}`],
|
|
495
|
+
dependsOn: [record.id],
|
|
496
|
+
priority: record.priority || "high",
|
|
497
|
+
summary: `Clarification ${record.id} resolved from repo state`,
|
|
498
|
+
detail: resolution.guidance,
|
|
499
|
+
status: "resolved",
|
|
500
|
+
attempt,
|
|
501
|
+
source: "launcher",
|
|
502
|
+
});
|
|
503
|
+
changed = true;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (resolution?.type === "route") {
|
|
508
|
+
const routeCycles = linkedRequests.length;
|
|
509
|
+
const lastRouteAttempt = latestRouteAttempt(linkedRequests);
|
|
510
|
+
if (openLinkedRequests.length === 0) {
|
|
511
|
+
createClarificationRoute({
|
|
512
|
+
triagePath,
|
|
513
|
+
coordinationLogPath,
|
|
514
|
+
lanePaths,
|
|
515
|
+
wave,
|
|
516
|
+
record,
|
|
517
|
+
routeAgentId: resolution.routeAgentId,
|
|
518
|
+
guidance: resolution.guidance,
|
|
519
|
+
cycle: routeCycles + 1,
|
|
520
|
+
attempt,
|
|
521
|
+
});
|
|
522
|
+
changed = true;
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (attempt > lastRouteAttempt && routeCycles < MAX_ROUTED_CLARIFICATION_CYCLES) {
|
|
526
|
+
supersedeOpenRequests(coordinationLogPath, openLinkedRequests, attempt);
|
|
527
|
+
createClarificationRoute({
|
|
528
|
+
triagePath,
|
|
529
|
+
coordinationLogPath,
|
|
530
|
+
lanePaths,
|
|
531
|
+
wave,
|
|
532
|
+
record,
|
|
533
|
+
routeAgentId: resolution.routeAgentId,
|
|
534
|
+
guidance: resolution.guidance,
|
|
535
|
+
cycle: routeCycles + 1,
|
|
536
|
+
attempt,
|
|
537
|
+
});
|
|
538
|
+
changed = true;
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (attempt > lastRouteAttempt && routeCycles >= MAX_ROUTED_CLARIFICATION_CYCLES) {
|
|
542
|
+
if (openEscalationForClarification(coordinationState, record.id)) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const escalationRecord = escalateClarificationToHuman({
|
|
546
|
+
triagePath,
|
|
547
|
+
lanePaths,
|
|
548
|
+
wave,
|
|
549
|
+
record,
|
|
550
|
+
orchestratorId,
|
|
551
|
+
attempt,
|
|
552
|
+
reason:
|
|
553
|
+
resolution.guidance ||
|
|
554
|
+
`Clarification remained unresolved after ${MAX_ROUTED_CLARIFICATION_CYCLES} routed cycles.`,
|
|
555
|
+
});
|
|
556
|
+
appendCoordinationRecord(coordinationLogPath, escalationRecord);
|
|
557
|
+
updateClarificationRecord(
|
|
558
|
+
coordinationLogPath,
|
|
559
|
+
record,
|
|
560
|
+
"in_progress",
|
|
561
|
+
`Escalated to human via ${escalationRecord.artifactRefs[0]}.`,
|
|
562
|
+
attempt,
|
|
563
|
+
);
|
|
564
|
+
changed = true;
|
|
565
|
+
}
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (openEscalationForClarification(coordinationState, record.id)) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const escalationRecord = escalateClarificationToHuman({
|
|
573
|
+
triagePath,
|
|
574
|
+
lanePaths,
|
|
575
|
+
wave,
|
|
576
|
+
record,
|
|
577
|
+
orchestratorId,
|
|
578
|
+
attempt,
|
|
579
|
+
reason: record.detail || "No repo-state or owner resolution was available.",
|
|
580
|
+
});
|
|
581
|
+
appendCoordinationRecord(coordinationLogPath, escalationRecord);
|
|
582
|
+
updateClarificationRecord(
|
|
583
|
+
coordinationLogPath,
|
|
584
|
+
record,
|
|
585
|
+
"in_progress",
|
|
586
|
+
`Escalated to human via ${escalationRecord.artifactRefs[0]}.`,
|
|
587
|
+
attempt,
|
|
588
|
+
);
|
|
589
|
+
changed = true;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const refreshedTriageState = fs.existsSync(triagePath)
|
|
593
|
+
? readMaterializedCoordinationState(triagePath)
|
|
594
|
+
: {
|
|
595
|
+
byId: new Map(),
|
|
596
|
+
humanEscalations: [],
|
|
597
|
+
};
|
|
598
|
+
writePendingHumanSummary(pendingHumanPath(lanePaths, wave.wave), refreshedTriageState);
|
|
599
|
+
return {
|
|
600
|
+
changed,
|
|
601
|
+
triagePath,
|
|
602
|
+
pendingHumanPath: pendingHumanPath(lanePaths, wave.wave),
|
|
603
|
+
state: refreshedTriageState,
|
|
604
|
+
};
|
|
605
|
+
}
|