@chllming/wave-orchestration 0.5.3 → 0.6.0
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 +53 -3
- package/README.md +81 -506
- package/docs/README.md +53 -0
- package/docs/agents/wave-cont-eval-role.md +36 -0
- package/docs/agents/{wave-evaluator-role.md → wave-cont-qa-role.md} +14 -11
- package/docs/agents/wave-documentation-role.md +1 -1
- package/docs/agents/wave-infra-role.md +1 -1
- package/docs/agents/wave-integration-role.md +3 -3
- package/docs/agents/wave-launcher-role.md +4 -3
- package/docs/agents/wave-security-role.md +40 -0
- package/docs/concepts/context7-vs-skills.md +94 -0
- package/docs/concepts/operating-modes.md +91 -0
- package/docs/concepts/runtime-agnostic-orchestration.md +95 -0
- package/docs/concepts/what-is-a-wave.md +183 -0
- package/docs/evals/README.md +166 -0
- package/docs/evals/benchmark-catalog.json +663 -0
- package/docs/guides/author-and-run-waves.md +135 -0
- package/docs/guides/planner.md +118 -0
- package/docs/guides/terminal-surfaces.md +82 -0
- package/docs/image.png +0 -0
- package/docs/plans/component-cutover-matrix.json +1 -1
- package/docs/plans/component-cutover-matrix.md +1 -1
- package/docs/plans/context7-wave-orchestrator.md +2 -0
- package/docs/plans/current-state.md +29 -1
- package/docs/plans/examples/wave-example-live-proof.md +435 -0
- package/docs/plans/master-plan.md +3 -3
- package/docs/plans/migration.md +46 -3
- package/docs/plans/wave-orchestrator.md +71 -8
- package/docs/plans/waves/wave-0.md +4 -4
- package/docs/reference/live-proof-waves.md +177 -0
- package/docs/reference/migration-0.2-to-0.5.md +26 -19
- package/docs/reference/npmjs-trusted-publishing.md +6 -5
- package/docs/reference/runtime-config/README.md +29 -0
- package/docs/reference/sample-waves.md +87 -0
- package/docs/reference/skills.md +224 -0
- package/docs/research/agent-context-sources.md +130 -11
- package/docs/research/coordination-failure-review.md +266 -0
- package/docs/roadmap.md +164 -564
- package/package.json +3 -2
- package/releases/manifest.json +37 -2
- package/scripts/research/agent-context-archive.mjs +83 -1
- package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +811 -0
- package/scripts/wave-orchestrator/adhoc.mjs +1331 -0
- package/scripts/wave-orchestrator/agent-state.mjs +358 -6
- package/scripts/wave-orchestrator/artifact-schemas.mjs +173 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +10 -3
- package/scripts/wave-orchestrator/config.mjs +65 -12
- package/scripts/wave-orchestrator/context7.mjs +11 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +51 -19
- package/scripts/wave-orchestrator/coordination-store.mjs +26 -4
- package/scripts/wave-orchestrator/coordination.mjs +99 -9
- package/scripts/wave-orchestrator/dashboard-state.mjs +20 -8
- package/scripts/wave-orchestrator/dep-cli.mjs +5 -2
- package/scripts/wave-orchestrator/docs-queue.mjs +8 -2
- package/scripts/wave-orchestrator/evals.mjs +451 -0
- package/scripts/wave-orchestrator/executors.mjs +24 -11
- package/scripts/wave-orchestrator/feedback.mjs +15 -1
- package/scripts/wave-orchestrator/install.mjs +69 -7
- package/scripts/wave-orchestrator/launcher-closure.mjs +281 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +334 -0
- package/scripts/wave-orchestrator/launcher.mjs +778 -577
- package/scripts/wave-orchestrator/ledger.mjs +123 -20
- package/scripts/wave-orchestrator/local-executor.mjs +99 -12
- package/scripts/wave-orchestrator/planner.mjs +1463 -0
- package/scripts/wave-orchestrator/project-profile.mjs +190 -0
- package/scripts/wave-orchestrator/replay.mjs +6 -3
- package/scripts/wave-orchestrator/role-helpers.mjs +84 -0
- package/scripts/wave-orchestrator/shared.mjs +77 -11
- package/scripts/wave-orchestrator/skills.mjs +979 -0
- package/scripts/wave-orchestrator/terminals.mjs +16 -0
- package/scripts/wave-orchestrator/traces.mjs +73 -27
- package/scripts/wave-orchestrator/wave-files.mjs +1224 -163
- package/scripts/wave.mjs +20 -0
- package/skills/README.md +202 -0
- package/skills/provider-aws/SKILL.md +117 -0
- package/skills/provider-aws/adapters/claude.md +1 -0
- package/skills/provider-aws/adapters/codex.md +1 -0
- package/skills/provider-aws/references/service-verification.md +39 -0
- package/skills/provider-aws/skill.json +54 -0
- package/skills/provider-custom-deploy/SKILL.md +64 -0
- package/skills/provider-custom-deploy/skill.json +50 -0
- package/skills/provider-docker-compose/SKILL.md +96 -0
- package/skills/provider-docker-compose/adapters/local.md +1 -0
- package/skills/provider-docker-compose/skill.json +53 -0
- package/skills/provider-github-release/SKILL.md +121 -0
- package/skills/provider-github-release/adapters/claude.md +1 -0
- package/skills/provider-github-release/adapters/codex.md +1 -0
- package/skills/provider-github-release/skill.json +55 -0
- package/skills/provider-kubernetes/SKILL.md +143 -0
- package/skills/provider-kubernetes/adapters/claude.md +1 -0
- package/skills/provider-kubernetes/adapters/codex.md +1 -0
- package/skills/provider-kubernetes/references/kubectl-patterns.md +58 -0
- package/skills/provider-kubernetes/skill.json +52 -0
- package/skills/provider-railway/SKILL.md +123 -0
- package/skills/provider-railway/adapters/claude.md +1 -0
- package/skills/provider-railway/adapters/codex.md +1 -0
- package/skills/provider-railway/adapters/local.md +1 -0
- package/skills/provider-railway/adapters/opencode.md +1 -0
- package/skills/provider-railway/references/verification-commands.md +39 -0
- package/skills/provider-railway/skill.json +71 -0
- package/skills/provider-ssh-manual/SKILL.md +97 -0
- package/skills/provider-ssh-manual/skill.json +54 -0
- package/skills/repo-coding-rules/SKILL.md +91 -0
- package/skills/repo-coding-rules/skill.json +34 -0
- package/skills/role-cont-eval/SKILL.md +90 -0
- package/skills/role-cont-eval/adapters/codex.md +1 -0
- package/skills/role-cont-eval/skill.json +36 -0
- package/skills/role-cont-qa/SKILL.md +93 -0
- package/skills/role-cont-qa/adapters/claude.md +1 -0
- package/skills/role-cont-qa/skill.json +36 -0
- package/skills/role-deploy/SKILL.md +96 -0
- package/skills/role-deploy/skill.json +36 -0
- package/skills/role-documentation/SKILL.md +72 -0
- package/skills/role-documentation/skill.json +36 -0
- package/skills/role-implementation/SKILL.md +68 -0
- package/skills/role-implementation/skill.json +36 -0
- package/skills/role-infra/SKILL.md +80 -0
- package/skills/role-infra/skill.json +36 -0
- package/skills/role-integration/SKILL.md +84 -0
- package/skills/role-integration/skill.json +36 -0
- package/skills/role-research/SKILL.md +64 -0
- package/skills/role-research/skill.json +36 -0
- package/skills/role-security/SKILL.md +60 -0
- package/skills/role-security/skill.json +36 -0
- package/skills/runtime-claude/SKILL.md +65 -0
- package/skills/runtime-claude/skill.json +36 -0
- package/skills/runtime-codex/SKILL.md +57 -0
- package/skills/runtime-codex/skill.json +36 -0
- package/skills/runtime-local/SKILL.md +44 -0
- package/skills/runtime-local/skill.json +36 -0
- package/skills/runtime-opencode/SKILL.md +57 -0
- package/skills/runtime-opencode/skill.json +36 -0
- package/skills/wave-core/SKILL.md +114 -0
- package/skills/wave-core/references/marker-syntax.md +62 -0
- package/skills/wave-core/skill.json +35 -0
- package/wave.config.json +61 -5
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadWaveConfig } from "./config.mjs";
|
|
3
|
+
import { REPO_ROOT, ensureDirectory, readJsonOrNull, writeJsonAtomic } from "./shared.mjs";
|
|
4
|
+
import { normalizeTerminalSurface } from "./terminals.mjs";
|
|
5
|
+
|
|
6
|
+
export const PROJECT_PROFILE_SCHEMA_VERSION = 1;
|
|
7
|
+
export const PROJECT_PROFILE_PATH = path.join(REPO_ROOT, ".wave", "project-profile.json");
|
|
8
|
+
export const PROJECT_OVERSIGHT_MODES = ["oversight", "dark-factory"];
|
|
9
|
+
export const PROJECT_PROFILE_TERMINAL_SURFACES = ["vscode", "tmux"];
|
|
10
|
+
export const DEPLOY_ENVIRONMENT_KINDS = [
|
|
11
|
+
"railway-mcp",
|
|
12
|
+
"railway-cli",
|
|
13
|
+
"docker-compose",
|
|
14
|
+
"kubernetes",
|
|
15
|
+
"ssh-manual",
|
|
16
|
+
"custom",
|
|
17
|
+
];
|
|
18
|
+
export const DRAFT_TEMPLATES = ["implementation", "qa", "infra", "release"];
|
|
19
|
+
|
|
20
|
+
function cleanText(value) {
|
|
21
|
+
return String(value ?? "").trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizePathForProfile(value) {
|
|
25
|
+
const normalized = cleanText(value).replaceAll("\\", "/");
|
|
26
|
+
if (!normalized) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return path.isAbsolute(normalized) ? path.relative(REPO_ROOT, normalized) : normalized;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeProjectTerminalSurface(value, label = "defaultTerminalSurface") {
|
|
33
|
+
const normalized = normalizeTerminalSurface(value, label);
|
|
34
|
+
return normalized === "none" ? "vscode" : normalized;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizeOversightMode(value, label = "oversight mode") {
|
|
38
|
+
const normalized = cleanText(value).toLowerCase();
|
|
39
|
+
if (!PROJECT_OVERSIGHT_MODES.includes(normalized)) {
|
|
40
|
+
throw new Error(`${label} must be one of: ${PROJECT_OVERSIGHT_MODES.join(", ")}`);
|
|
41
|
+
}
|
|
42
|
+
return normalized;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeDraftTemplate(value, label = "draft template") {
|
|
46
|
+
const normalized = cleanText(value).toLowerCase();
|
|
47
|
+
if (!DRAFT_TEMPLATES.includes(normalized)) {
|
|
48
|
+
throw new Error(`${label} must be one of: ${DRAFT_TEMPLATES.join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeDeployEnvironment(rawEnvironment, index) {
|
|
54
|
+
if (!rawEnvironment || typeof rawEnvironment !== "object" || Array.isArray(rawEnvironment)) {
|
|
55
|
+
throw new Error(`deployEnvironments[${index}] must be an object`);
|
|
56
|
+
}
|
|
57
|
+
const id = cleanText(rawEnvironment.id).toLowerCase();
|
|
58
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(id)) {
|
|
59
|
+
throw new Error(`deployEnvironments[${index}].id must be a lowercase repo-safe identifier`);
|
|
60
|
+
}
|
|
61
|
+
const kind = cleanText(rawEnvironment.kind).toLowerCase();
|
|
62
|
+
if (!DEPLOY_ENVIRONMENT_KINDS.includes(kind)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`deployEnvironments[${index}].kind must be one of: ${DEPLOY_ENVIRONMENT_KINDS.join(", ")}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
id,
|
|
69
|
+
name: cleanText(rawEnvironment.name) || id,
|
|
70
|
+
kind,
|
|
71
|
+
isDefault: rawEnvironment.isDefault === true,
|
|
72
|
+
notes: cleanText(rawEnvironment.notes) || null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function buildDefaultProjectProfile(config = loadWaveConfig()) {
|
|
77
|
+
const now = new Date().toISOString();
|
|
78
|
+
return {
|
|
79
|
+
schemaVersion: PROJECT_PROFILE_SCHEMA_VERSION,
|
|
80
|
+
initializedAt: now,
|
|
81
|
+
updatedAt: now,
|
|
82
|
+
newProject: false,
|
|
83
|
+
defaultOversightMode: "oversight",
|
|
84
|
+
defaultTerminalSurface: "vscode",
|
|
85
|
+
deployEnvironments: [],
|
|
86
|
+
plannerDefaults: {
|
|
87
|
+
template: "implementation",
|
|
88
|
+
lane: cleanText(config.defaultLane) || "main",
|
|
89
|
+
},
|
|
90
|
+
source: {
|
|
91
|
+
projectName: cleanText(config.projectName) || "Wave Orchestrator",
|
|
92
|
+
configPath: normalizePathForProfile(config.configPath || "wave.config.json"),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function normalizeProjectProfile(rawProfile, options = {}) {
|
|
98
|
+
if (!rawProfile || typeof rawProfile !== "object" || Array.isArray(rawProfile)) {
|
|
99
|
+
throw new Error(`Project profile is invalid: ${path.relative(REPO_ROOT, PROJECT_PROFILE_PATH)}`);
|
|
100
|
+
}
|
|
101
|
+
const config = options.config || loadWaveConfig();
|
|
102
|
+
const base = buildDefaultProjectProfile(config);
|
|
103
|
+
const deployEnvironments = Array.isArray(rawProfile.deployEnvironments)
|
|
104
|
+
? rawProfile.deployEnvironments.map((entry, index) => normalizeDeployEnvironment(entry, index))
|
|
105
|
+
: [];
|
|
106
|
+
const defaultEnvironmentIndex = deployEnvironments.findIndex((entry) => entry.isDefault);
|
|
107
|
+
if (defaultEnvironmentIndex === -1 && deployEnvironments.length > 0) {
|
|
108
|
+
deployEnvironments[0].isDefault = true;
|
|
109
|
+
} else if (defaultEnvironmentIndex > -1) {
|
|
110
|
+
deployEnvironments.forEach((entry, index) => {
|
|
111
|
+
entry.isDefault = index === defaultEnvironmentIndex;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const plannerDefaults =
|
|
115
|
+
rawProfile.plannerDefaults && typeof rawProfile.plannerDefaults === "object"
|
|
116
|
+
? rawProfile.plannerDefaults
|
|
117
|
+
: {};
|
|
118
|
+
return {
|
|
119
|
+
schemaVersion: PROJECT_PROFILE_SCHEMA_VERSION,
|
|
120
|
+
initializedAt: cleanText(rawProfile.initializedAt) || base.initializedAt,
|
|
121
|
+
updatedAt: cleanText(rawProfile.updatedAt) || base.updatedAt,
|
|
122
|
+
newProject: rawProfile.newProject === true,
|
|
123
|
+
defaultOversightMode: normalizeOversightMode(
|
|
124
|
+
rawProfile.defaultOversightMode || base.defaultOversightMode,
|
|
125
|
+
"defaultOversightMode",
|
|
126
|
+
),
|
|
127
|
+
defaultTerminalSurface: normalizeProjectTerminalSurface(
|
|
128
|
+
rawProfile.defaultTerminalSurface || base.defaultTerminalSurface,
|
|
129
|
+
"defaultTerminalSurface",
|
|
130
|
+
),
|
|
131
|
+
deployEnvironments,
|
|
132
|
+
plannerDefaults: {
|
|
133
|
+
template: normalizeDraftTemplate(
|
|
134
|
+
plannerDefaults.template || base.plannerDefaults.template,
|
|
135
|
+
"plannerDefaults.template",
|
|
136
|
+
),
|
|
137
|
+
lane: cleanText(plannerDefaults.lane) || base.plannerDefaults.lane,
|
|
138
|
+
},
|
|
139
|
+
source: {
|
|
140
|
+
projectName: cleanText(rawProfile.source?.projectName) || base.source.projectName,
|
|
141
|
+
configPath: normalizePathForProfile(rawProfile.source?.configPath) || base.source.configPath,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function readProjectProfile(options = {}) {
|
|
147
|
+
const payload = readJsonOrNull(PROJECT_PROFILE_PATH);
|
|
148
|
+
if (!payload) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return normalizeProjectProfile(payload, options);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function writeProjectProfile(profile, options = {}) {
|
|
155
|
+
const config = options.config || loadWaveConfig();
|
|
156
|
+
const now = new Date().toISOString();
|
|
157
|
+
const normalized = normalizeProjectProfile(
|
|
158
|
+
{
|
|
159
|
+
...profile,
|
|
160
|
+
initializedAt: profile?.initializedAt || now,
|
|
161
|
+
updatedAt: now,
|
|
162
|
+
source: {
|
|
163
|
+
projectName: profile?.source?.projectName || config.projectName,
|
|
164
|
+
configPath: normalizePathForProfile(config.configPath || "wave.config.json"),
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{ config },
|
|
168
|
+
);
|
|
169
|
+
ensureDirectory(path.dirname(PROJECT_PROFILE_PATH));
|
|
170
|
+
writeJsonAtomic(PROJECT_PROFILE_PATH, normalized);
|
|
171
|
+
return normalized;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function updateProjectProfile(mutator, options = {}) {
|
|
175
|
+
const config = options.config || loadWaveConfig();
|
|
176
|
+
const current = readProjectProfile({ config }) || buildDefaultProjectProfile(config);
|
|
177
|
+
const next = typeof mutator === "function" ? mutator(current) : { ...current, ...(mutator || {}) };
|
|
178
|
+
return writeProjectProfile(
|
|
179
|
+
{
|
|
180
|
+
...current,
|
|
181
|
+
...(next || {}),
|
|
182
|
+
initializedAt: current.initializedAt,
|
|
183
|
+
},
|
|
184
|
+
{ config },
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function resolveDefaultTerminalSurface(profile) {
|
|
189
|
+
return normalizeProjectTerminalSurface(profile?.defaultTerminalSurface || "vscode");
|
|
190
|
+
}
|
|
@@ -22,19 +22,22 @@ function buildReplayLanePaths(metadata) {
|
|
|
22
22
|
: null;
|
|
23
23
|
const roles = replayContext?.roles || metadata?.roles || {};
|
|
24
24
|
const validation = replayContext?.validation || metadata?.validation || {};
|
|
25
|
-
const
|
|
25
|
+
const contQaAgentId = roles.contQaAgentId || roles.evaluatorAgentId || "A0";
|
|
26
|
+
const contEvalAgentId = roles.contEvalAgentId || "E0";
|
|
26
27
|
const integrationAgentId = roles.integrationAgentId || "A8";
|
|
27
28
|
const documentationAgentId = roles.documentationAgentId || "A9";
|
|
28
29
|
return {
|
|
29
30
|
lane: replayContext?.lane || metadata?.lane || "main",
|
|
30
|
-
|
|
31
|
+
contQaAgentId,
|
|
32
|
+
contEvalAgentId,
|
|
31
33
|
integrationAgentId,
|
|
32
34
|
documentationAgentId,
|
|
33
35
|
requireIntegrationStewardFromWave:
|
|
34
36
|
validation.requireIntegrationStewardFromWave ?? null,
|
|
35
37
|
laneProfile: {
|
|
36
38
|
roles: {
|
|
37
|
-
|
|
39
|
+
contQaAgentId,
|
|
40
|
+
contEvalAgentId,
|
|
38
41
|
integrationAgentId,
|
|
39
42
|
documentationAgentId,
|
|
40
43
|
},
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONT_EVAL_AGENT_ID,
|
|
3
|
+
DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
4
|
+
} from "./config.mjs";
|
|
5
|
+
|
|
6
|
+
function cleanPath(value) {
|
|
7
|
+
return String(value || "")
|
|
8
|
+
.trim()
|
|
9
|
+
.replaceAll("\\", "/");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isContQaReportPath(relPath) {
|
|
13
|
+
return /(?:^|\/)(?:reviews?|.*cont[-_]?qa).*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isContEvalReportPath(relPath) {
|
|
17
|
+
return /(?:^|\/)(?:reviews?|.*cont[-_]?eval|.*eval).*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isSecurityRolePromptPath(
|
|
21
|
+
relPath,
|
|
22
|
+
securityRolePromptPath = DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
23
|
+
) {
|
|
24
|
+
const normalized = cleanPath(relPath);
|
|
25
|
+
const configured = cleanPath(securityRolePromptPath);
|
|
26
|
+
return (
|
|
27
|
+
normalized === configured ||
|
|
28
|
+
normalized === DEFAULT_SECURITY_ROLE_PROMPT_PATH ||
|
|
29
|
+
normalized.endsWith("/wave-security-role.md")
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isSecurityReportPath(relPath) {
|
|
34
|
+
return /(?:^|\/).*security.*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isContEvalImplementationOwningAgent(
|
|
38
|
+
agent,
|
|
39
|
+
{ contEvalAgentId = DEFAULT_CONT_EVAL_AGENT_ID } = {},
|
|
40
|
+
) {
|
|
41
|
+
if (!agent || agent.agentId !== contEvalAgentId) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const ownedPaths = Array.isArray(agent.ownedPaths) ? agent.ownedPaths.map(cleanPath).filter(Boolean) : [];
|
|
45
|
+
if (ownedPaths.length === 0) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return ownedPaths.some((ownedPath) => !isContEvalReportPath(ownedPath));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isContEvalReportOnlyAgent(
|
|
52
|
+
agent,
|
|
53
|
+
{ contEvalAgentId = DEFAULT_CONT_EVAL_AGENT_ID } = {},
|
|
54
|
+
) {
|
|
55
|
+
return agent?.agentId === contEvalAgentId && !isContEvalImplementationOwningAgent(agent, {
|
|
56
|
+
contEvalAgentId,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isSecurityReviewAgent(
|
|
61
|
+
agent,
|
|
62
|
+
{ securityRolePromptPath = DEFAULT_SECURITY_ROLE_PROMPT_PATH } = {},
|
|
63
|
+
) {
|
|
64
|
+
if (!agent || typeof agent !== "object") {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const rolePromptPaths = Array.isArray(agent.rolePromptPaths) ? agent.rolePromptPaths : [];
|
|
68
|
+
if (
|
|
69
|
+
rolePromptPaths.some((rolePromptPath) =>
|
|
70
|
+
isSecurityRolePromptPath(rolePromptPath, securityRolePromptPath),
|
|
71
|
+
)
|
|
72
|
+
) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
const capabilities = Array.isArray(agent.capabilities)
|
|
76
|
+
? agent.capabilities.map((entry) => String(entry || "").trim().toLowerCase())
|
|
77
|
+
: [];
|
|
78
|
+
return capabilities.includes("security-review");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveSecurityReviewReportPath(agent) {
|
|
82
|
+
const ownedPaths = Array.isArray(agent?.ownedPaths) ? agent.ownedPaths.map(cleanPath).filter(Boolean) : [];
|
|
83
|
+
return ownedPaths.find((ownedPath) => isSecurityReportPath(ownedPath)) || null;
|
|
84
|
+
}
|
|
@@ -77,22 +77,71 @@ export function sanitizeOrchestratorId(value) {
|
|
|
77
77
|
return id.slice(0, 64);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
export function sanitizeAdhocRunId(value) {
|
|
81
|
+
const id = String(value || "")
|
|
82
|
+
.trim()
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
85
|
+
.replace(/-+/g, "-")
|
|
86
|
+
.replace(/^-+|-+$/g, "");
|
|
87
|
+
if (!id) {
|
|
88
|
+
throw new Error("Ad-hoc run ID is required");
|
|
89
|
+
}
|
|
90
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(id)) {
|
|
91
|
+
throw new Error(`Invalid ad-hoc run ID: ${value}`);
|
|
92
|
+
}
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function buildWorkspaceTmuxToken(workspaceRoot = REPO_ROOT) {
|
|
97
|
+
const repoBase =
|
|
98
|
+
path
|
|
99
|
+
.basename(path.resolve(String(workspaceRoot || REPO_ROOT)))
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
102
|
+
.replace(/^_+|_+$/g, "")
|
|
103
|
+
.slice(0, 12) || "repo";
|
|
104
|
+
const repoHash = crypto
|
|
105
|
+
.createHash("sha1")
|
|
106
|
+
.update(path.resolve(String(workspaceRoot || REPO_ROOT)))
|
|
107
|
+
.digest("hex")
|
|
108
|
+
.slice(0, 8);
|
|
109
|
+
return `${repoBase}_${repoHash}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
80
112
|
export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
81
113
|
const config = options.config || loadWaveConfig();
|
|
82
|
-
const
|
|
114
|
+
const baseLaneProfile = resolveLaneProfile(config, laneInput || config.defaultLane);
|
|
115
|
+
const adhocRunId = options.adhocRunId ? sanitizeAdhocRunId(options.adhocRunId) : null;
|
|
116
|
+
const laneProfile = adhocRunId
|
|
117
|
+
? {
|
|
118
|
+
...baseLaneProfile,
|
|
119
|
+
validation: {
|
|
120
|
+
...baseLaneProfile.validation,
|
|
121
|
+
requireComponentPromotionsFromWave: null,
|
|
122
|
+
requireAgentComponentsFromWave: null,
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
: baseLaneProfile;
|
|
83
126
|
const lane = laneProfile.lane;
|
|
84
127
|
const laneTmux = lane.replace(/-/g, "_");
|
|
128
|
+
const runKind = adhocRunId ? "adhoc" : "roadmap";
|
|
85
129
|
const runVariant = String(options.runVariant || "")
|
|
86
130
|
.trim()
|
|
87
131
|
.toLowerCase();
|
|
88
132
|
if (runVariant && runVariant !== "dry-run") {
|
|
89
133
|
throw new Error(`Unsupported lane path variant: ${options.runVariant}`);
|
|
90
134
|
}
|
|
135
|
+
const workspaceTmuxToken = buildWorkspaceTmuxToken(REPO_ROOT);
|
|
91
136
|
const docsDir = path.join(REPO_ROOT, laneProfile.docsDir);
|
|
92
137
|
const plansDir = path.join(REPO_ROOT, laneProfile.plansDir);
|
|
93
138
|
const preferredWavesDir = path.join(REPO_ROOT, laneProfile.wavesDir);
|
|
94
139
|
const legacyWavesDir = path.join(docsDir, "waves");
|
|
95
|
-
const
|
|
140
|
+
const adhocRootDir = path.join(REPO_ROOT, ".wave", "adhoc");
|
|
141
|
+
const adhocRunDir = adhocRunId ? path.join(adhocRootDir, "runs", adhocRunId) : null;
|
|
142
|
+
const baseStateDir = adhocRunId
|
|
143
|
+
? path.join(REPO_ROOT, laneProfile.paths.stateRoot, `${lane}-wave-launcher`, "adhoc", adhocRunId)
|
|
144
|
+
: path.join(REPO_ROOT, laneProfile.paths.stateRoot, `${lane}-wave-launcher`);
|
|
96
145
|
const stateDir = runVariant === "dry-run" ? path.join(baseStateDir, "dry-run") : baseStateDir;
|
|
97
146
|
const orchestratorStateDir =
|
|
98
147
|
runVariant === "dry-run"
|
|
@@ -103,14 +152,24 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
103
152
|
config,
|
|
104
153
|
laneProfile,
|
|
105
154
|
lane,
|
|
155
|
+
runKind,
|
|
156
|
+
runId: adhocRunId,
|
|
106
157
|
runVariant,
|
|
107
158
|
docsDir,
|
|
108
159
|
plansDir,
|
|
109
160
|
wavesDir:
|
|
110
|
-
|
|
161
|
+
adhocRunDir ||
|
|
162
|
+
(fs.existsSync(preferredWavesDir) || !fs.existsSync(legacyWavesDir)
|
|
111
163
|
? preferredWavesDir
|
|
112
|
-
: legacyWavesDir,
|
|
164
|
+
: legacyWavesDir),
|
|
113
165
|
legacyWavesDir,
|
|
166
|
+
adhocRootDir,
|
|
167
|
+
adhocRunDir,
|
|
168
|
+
adhocIndexPath: path.join(adhocRootDir, "index.json"),
|
|
169
|
+
adhocRequestPath: adhocRunDir ? path.join(adhocRunDir, "request.json") : null,
|
|
170
|
+
adhocSpecPath: adhocRunDir ? path.join(adhocRunDir, "spec.json") : null,
|
|
171
|
+
adhocWavePath: adhocRunDir ? path.join(adhocRunDir, "wave-0.md") : null,
|
|
172
|
+
adhocResultPath: adhocRunDir ? path.join(adhocRunDir, "result.json") : null,
|
|
114
173
|
promptsDir: path.join(stateDir, "prompts"),
|
|
115
174
|
logsDir: path.join(stateDir, "logs"),
|
|
116
175
|
statusDir: path.join(stateDir, "status"),
|
|
@@ -121,6 +180,7 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
121
180
|
inboxesDir: path.join(stateDir, "inboxes"),
|
|
122
181
|
ledgerDir: path.join(stateDir, "ledger"),
|
|
123
182
|
integrationDir: path.join(stateDir, "integration"),
|
|
183
|
+
securityDir: path.join(stateDir, "security"),
|
|
124
184
|
dependencySnapshotsDir: path.join(stateDir, "dependencies"),
|
|
125
185
|
docsQueueDir: path.join(stateDir, "docs-queue"),
|
|
126
186
|
tracesDir: path.join(stateDir, "traces"),
|
|
@@ -128,7 +188,9 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
128
188
|
executorOverlaysDir: path.join(stateDir, "executors"),
|
|
129
189
|
stateDir,
|
|
130
190
|
terminalsPath: path.join(REPO_ROOT, laneProfile.paths.terminalsPath),
|
|
191
|
+
skillsDir: path.join(REPO_ROOT, laneProfile.skills?.dir || "skills"),
|
|
131
192
|
context7BundleIndexPath: path.join(REPO_ROOT, laneProfile.paths.context7BundleIndexPath),
|
|
193
|
+
benchmarkCatalogPath: path.join(REPO_ROOT, laneProfile.paths.benchmarkCatalogPath),
|
|
132
194
|
componentCutoverMatrixDocPath: path.join(
|
|
133
195
|
REPO_ROOT,
|
|
134
196
|
laneProfile.paths.componentCutoverMatrixDocPath,
|
|
@@ -137,15 +199,18 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
137
199
|
REPO_ROOT,
|
|
138
200
|
laneProfile.paths.componentCutoverMatrixJsonPath,
|
|
139
201
|
),
|
|
140
|
-
sharedPlanDocs: laneProfile.sharedPlanDocs,
|
|
202
|
+
sharedPlanDocs: laneProfile.sharedPlanDocs || [],
|
|
141
203
|
requiredPromptReferences: laneProfile.validation.requiredPromptReferences,
|
|
142
204
|
rolePromptDir: laneProfile.roles.rolePromptDir,
|
|
143
|
-
|
|
205
|
+
contQaAgentId: laneProfile.roles.contQaAgentId,
|
|
206
|
+
contEvalAgentId: laneProfile.roles.contEvalAgentId,
|
|
144
207
|
integrationAgentId: laneProfile.roles.integrationAgentId,
|
|
145
208
|
documentationAgentId: laneProfile.roles.documentationAgentId,
|
|
146
|
-
|
|
209
|
+
contQaRolePromptPath: laneProfile.roles.contQaRolePromptPath,
|
|
210
|
+
contEvalRolePromptPath: laneProfile.roles.contEvalRolePromptPath,
|
|
147
211
|
integrationRolePromptPath: laneProfile.roles.integrationRolePromptPath,
|
|
148
212
|
documentationRolePromptPath: laneProfile.roles.documentationRolePromptPath,
|
|
213
|
+
securityRolePromptPath: laneProfile.roles.securityRolePromptPath,
|
|
149
214
|
requireDocumentationStewardFromWave:
|
|
150
215
|
laneProfile.validation.requireDocumentationStewardFromWave,
|
|
151
216
|
requireContext7DeclarationsFromWave:
|
|
@@ -157,6 +222,7 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
157
222
|
laneProfile.validation.requireComponentPromotionsFromWave,
|
|
158
223
|
requireAgentComponentsFromWave: laneProfile.validation.requireAgentComponentsFromWave,
|
|
159
224
|
executors: laneProfile.executors,
|
|
225
|
+
skills: laneProfile.skills,
|
|
160
226
|
capabilityRouting: laneProfile.capabilityRouting,
|
|
161
227
|
defaultManifestPath: path.join(stateDir, "waves.manifest.json"),
|
|
162
228
|
defaultRunStatePath: path.join(stateDir, "run-state.json"),
|
|
@@ -165,10 +231,10 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
|
|
|
165
231
|
terminalNamePrefix: `${lane}-wave`,
|
|
166
232
|
dashboardTerminalNamePrefix: `${lane}-wave-dashboard`,
|
|
167
233
|
globalDashboardTerminalName: `${lane}-wave-dashboard-global`,
|
|
168
|
-
tmuxSessionPrefix: `oc_${laneTmux}_wave`,
|
|
169
|
-
tmuxDashboardSessionPrefix: `oc_${laneTmux}_wave_dashboard`,
|
|
170
|
-
tmuxGlobalDashboardSessionPrefix: `oc_${laneTmux}_wave_dashboard_global`,
|
|
171
|
-
tmuxSocketName: `oc_${laneTmux}_waves`,
|
|
234
|
+
tmuxSessionPrefix: `oc_${laneTmux}_${workspaceTmuxToken}_wave`,
|
|
235
|
+
tmuxDashboardSessionPrefix: `oc_${laneTmux}_${workspaceTmuxToken}_wave_dashboard`,
|
|
236
|
+
tmuxGlobalDashboardSessionPrefix: `oc_${laneTmux}_${workspaceTmuxToken}_wave_dashboard_global`,
|
|
237
|
+
tmuxSocketName: `oc_${laneTmux}_${workspaceTmuxToken}_waves`,
|
|
172
238
|
orchestratorStateDir,
|
|
173
239
|
defaultOrchestratorBoardPath: path.join(
|
|
174
240
|
orchestratorStateDir,
|