@cassiomc1/forgeloop 1.12.0 → 1.14.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/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +1 -1
- package/AGENT_COMPATIBILITY.md +8 -0
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +90 -0
- package/DOCS_INDEX.md +46 -12
- package/ENG/c-development-eng.md +112 -0
- package/ENG/cpp-development-eng.md +109 -0
- package/ENG/dotnet-aspnetcore-development-eng.md +401 -0
- package/ENG/go-development-eng.md +103 -0
- package/ENG/java-development-eng.md +125 -0
- package/ENG/nodejs-backend-development-eng.md +605 -0
- package/ENG/php-development-eng.md +104 -0
- package/ENG/rust-development-eng.md +422 -0
- package/ENG/sec-code-eng.md +7 -7
- package/ENG/sql-development-eng.md +108 -0
- package/ENG/swift-development-eng.md +111 -0
- package/ENG/typescript-development-eng.md +108 -0
- package/EXECUTION_STATE.md +12 -0
- package/GUIDE_ROUTER.md +418 -9
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/QUALITY_SCORECARD.md +1 -0
- package/README.md +78 -52
- package/TERMINOLOGY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +19 -7
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +22 -4
- package/completions/forgeloop.bash +40 -4
- package/completions/forgeloop.fish +130 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +81 -3
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +123 -0
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +392 -10
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +34 -12
- package/docs/GETTING_STARTED.md +59 -0
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +60 -19
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +32 -0
- package/docs/RELEASE_CHECKLIST.md +66 -5
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +298 -3
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +1397 -0
- package/docs/protocol-requirements.json +101 -0
- package/package.json +46 -4
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/routing-input.schema.json +1 -1
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +84 -11
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +40 -1
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +313 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +282 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +108 -0
- package/src/commands/checkpoint-revalidate.js +176 -0
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +264 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +15 -0
- package/src/commands/gate-record.js +205 -0
- package/src/commands/gate-revalidate.js +137 -0
- package/src/commands/model-route.js +32 -0
- package/src/commands/next.js +19 -7
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-create.js +84 -25
- package/src/commands/task-list.js +22 -2
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +10 -1
- package/src/config/guides.json +44 -0
- package/src/core/artifact-registry.js +24 -0
- package/src/core/audit-ux.js +514 -0
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/build-script.js +151 -0
- package/src/core/c-cpp-project.js +143 -0
- package/src/core/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +249 -1
- package/src/core/command-executors.js +115 -3
- package/src/core/command-input.js +212 -102
- package/src/core/completion-artifacts.js +14 -5
- package/src/core/completion.js +4 -6
- package/src/core/config.js +3 -0
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/contract-bootstrap-recovery.js +655 -0
- package/src/core/contract-presets.js +82 -0
- package/src/core/contract-revision.js +210 -0
- package/src/core/decision/artifact.js +69 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +101 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +130 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +156 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +30 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/error-codes.js +281 -3
- package/src/core/events.js +226 -57
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +14 -0
- package/src/core/execution-profile.js +63 -38
- package/src/core/filesystem.js +1 -10
- package/src/core/gate-provenance.js +124 -0
- package/src/core/go-project.js +206 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- package/src/core/java-project.js +403 -0
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/multi-language-project.js +117 -0
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +130 -42
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +7 -2
- package/src/core/next-action.js +35 -7
- package/src/core/next-explanation.js +63 -0
- package/src/core/phase.js +128 -10
- package/src/core/php-project.js +85 -0
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/project-detection.js +1760 -52
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +132 -53
- package/src/core/recovery-history.js +1 -0
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +15 -1
- package/src/core/router.js +223 -4
- package/src/core/runtime-context.js +118 -61
- package/src/core/rust-project.js +400 -0
- package/src/core/schema-validation.js +3 -0
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/sources.js +37 -0
- package/src/core/sql-project.js +141 -0
- package/src/core/swift-project.js +200 -0
- package/src/core/task-claim-state.js +201 -1
- package/src/core/task-conflict-inspection.js +31 -5
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/templates.js +3 -0
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +90 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +11 -7
- package/src/core/transaction.js +1 -0
- package/src/core/typescript-project.js +349 -0
- package/src/core/xml-structure.js +123 -0
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
package/src/core/router.js
CHANGED
|
@@ -62,7 +62,21 @@ const SIGNALS = Object.freeze({
|
|
|
62
62
|
platforms: new Set(["web", "mobile", "desktop", "server", "ci", "cross-platform"]),
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
const PROJECT_FRAMEWORKS = new Set([
|
|
65
|
+
const PROJECT_FRAMEWORKS = new Set([
|
|
66
|
+
"flutter", "dotnet", "aspnetcore", "abp", "nodejs", "rust",
|
|
67
|
+
"c", "cpp", "java", "sql", "go", "typescript", "php", "swift",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const LANGUAGE_PROJECT_GUIDES = Object.freeze([
|
|
71
|
+
["c", "PROJECT_C_CONFIRMED"],
|
|
72
|
+
["cpp", "PROJECT_CPP_CONFIRMED"],
|
|
73
|
+
["java", "PROJECT_JAVA_CONFIRMED"],
|
|
74
|
+
["go", "PROJECT_GO_CONFIRMED"],
|
|
75
|
+
["typescript", "PROJECT_TYPESCRIPT_CONFIRMED"],
|
|
76
|
+
["php", "PROJECT_PHP_CONFIRMED"],
|
|
77
|
+
["swift", "PROJECT_SWIFT_CONFIRMED"],
|
|
78
|
+
["sql", "PROJECT_SQL_CONFIRMED"],
|
|
79
|
+
]);
|
|
66
80
|
|
|
67
81
|
export const PLATFORM_SEMANTICS = Object.freeze({
|
|
68
82
|
web: Object.freeze({
|
|
@@ -134,8 +148,100 @@ function addFlutterProjectGuides(input, add) {
|
|
|
134
148
|
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
135
149
|
|| !hasFlutterWorkContext(input.workType)) return;
|
|
136
150
|
add("flutter", "PROJECT_FLUTTER_SDK_DEPENDENCY");
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function hasDotNetWorkContext(workType) {
|
|
154
|
+
return !["documentation", "ui-copy"].includes(workType);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function addDotNetProjectGuides(input, add) {
|
|
158
|
+
const projectEvidence = input.projectEvidence;
|
|
159
|
+
if (!projectEvidence?.frameworks.includes("dotnet")
|
|
160
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
161
|
+
|| !hasDotNetWorkContext(input.workType)) return;
|
|
162
|
+
add("dotnet", "PROJECT_DOTNET_SDK_PROJECT");
|
|
163
|
+
if (projectEvidence.frameworks.includes("aspnetcore")) add("dotnet", "PROJECT_ASPNETCORE_CONFIRMED");
|
|
164
|
+
if (projectEvidence.frameworks.includes("abp")) add("dotnet", "PROJECT_ABP_CONFIRMED");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function hasNodeJsWorkContext(workType) {
|
|
168
|
+
return !["documentation", "ui-copy", "mobile-ui"].includes(workType);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function addNodeJsProjectGuides(input, add) {
|
|
172
|
+
const projectEvidence = input.projectEvidence;
|
|
173
|
+
if (!projectEvidence?.frameworks.includes("nodejs")
|
|
174
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
175
|
+
|| !hasNodeJsWorkContext(input.workType)) return;
|
|
176
|
+
add("nodejs", "PROJECT_NODEJS_CONFIRMED");
|
|
177
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":dependencies.") || signal.includes(":optionalDependencies."))) {
|
|
178
|
+
add("nodejs", "PROJECT_NODEJS_BACKEND_FRAMEWORK");
|
|
179
|
+
}
|
|
180
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":scripts."))) {
|
|
181
|
+
add("nodejs", "PROJECT_NODEJS_RUNTIME_SCRIPT");
|
|
182
|
+
}
|
|
183
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":import="))) {
|
|
184
|
+
add("nodejs", "PROJECT_NODEJS_SERVER_RUNTIME");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function hasRustWorkContext(workType) {
|
|
189
|
+
return !["documentation", "ui-copy", "mobile-ui"].includes(workType);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function addRustProjectGuides(input, add) {
|
|
193
|
+
const projectEvidence = input.projectEvidence;
|
|
194
|
+
if (!projectEvidence?.frameworks.includes("rust")
|
|
195
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
196
|
+
|| !hasRustWorkContext(input.workType)) return;
|
|
197
|
+
add("rust", "PROJECT_RUST_CONFIRMED");
|
|
198
|
+
if (projectEvidence.primarySignals.some((signal) => signal.endsWith(":package"))) {
|
|
199
|
+
add("rust", "PROJECT_RUST_CARGO_PACKAGE");
|
|
200
|
+
}
|
|
201
|
+
if (projectEvidence.primarySignals.some((signal) => signal.endsWith(":workspace"))) {
|
|
202
|
+
add("rust", "PROJECT_RUST_CARGO_WORKSPACE");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function hasLanguageWorkContext(workType) {
|
|
207
|
+
return !["documentation", "ui-copy"].includes(workType);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function addLanguageProjectGuides(input, add) {
|
|
211
|
+
const projectEvidence = input.projectEvidence;
|
|
212
|
+
if (!projectEvidence || !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
213
|
+
|| !hasLanguageWorkContext(input.workType)) return;
|
|
214
|
+
for (const [guide, reason] of LANGUAGE_PROJECT_GUIDES) {
|
|
215
|
+
if (projectEvidence.frameworks.includes(guide)) add(guide, reason);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function addProjectBaselineGuides(input, add) {
|
|
220
|
+
const projectEvidence = input.projectEvidence;
|
|
221
|
+
if (!projectEvidence || !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)) return;
|
|
222
|
+
if (projectEvidence.frameworks.includes("flutter") && hasFlutterWorkContext(input.workType)) {
|
|
223
|
+
add("clean", "PROJECT_FLUTTER_BASELINE");
|
|
224
|
+
add("test", "PROJECT_FLUTTER_BASELINE");
|
|
225
|
+
}
|
|
226
|
+
if (projectEvidence.frameworks.includes("dotnet") && hasDotNetWorkContext(input.workType)) {
|
|
227
|
+
add("clean", "PROJECT_DOTNET_BASELINE");
|
|
228
|
+
add("test", "PROJECT_DOTNET_BASELINE");
|
|
229
|
+
}
|
|
230
|
+
if (projectEvidence.frameworks.includes("nodejs") && hasNodeJsWorkContext(input.workType)) {
|
|
231
|
+
add("clean", "PROJECT_NODEJS_BASELINE");
|
|
232
|
+
add("test", "PROJECT_NODEJS_BASELINE");
|
|
233
|
+
}
|
|
234
|
+
if (projectEvidence.frameworks.includes("rust") && hasRustWorkContext(input.workType)) {
|
|
235
|
+
add("clean", "PROJECT_RUST_BASELINE");
|
|
236
|
+
add("test", "PROJECT_RUST_BASELINE");
|
|
237
|
+
}
|
|
238
|
+
if (hasLanguageWorkContext(input.workType)) {
|
|
239
|
+
for (const [language] of LANGUAGE_PROJECT_GUIDES) {
|
|
240
|
+
if (!projectEvidence.frameworks.includes(language)) continue;
|
|
241
|
+
add("clean", `PROJECT_${language.toUpperCase()}_BASELINE`);
|
|
242
|
+
add("test", `PROJECT_${language.toUpperCase()}_BASELINE`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
139
245
|
}
|
|
140
246
|
|
|
141
247
|
function flutterExclusionReason(projectEvidence, workType) {
|
|
@@ -146,6 +252,30 @@ function flutterExclusionReason(projectEvidence, workType) {
|
|
|
146
252
|
return "NO_FLUTTER_PRIMARY_EVIDENCE";
|
|
147
253
|
}
|
|
148
254
|
|
|
255
|
+
function dotNetExclusionReason(projectEvidence, workType) {
|
|
256
|
+
if (!projectEvidence) return "NO_DOTNET_PROJECT_EVIDENCE";
|
|
257
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_DOTNET_SCOPE_MATCH";
|
|
258
|
+
if (!projectEvidence.frameworks.includes("dotnet")) return "NO_DOTNET_PRIMARY_EVIDENCE";
|
|
259
|
+
if (!hasDotNetWorkContext(workType)) return "NO_DOTNET_EXECUTABLE_WORK";
|
|
260
|
+
return "NO_DOTNET_PRIMARY_EVIDENCE";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function nodeJsExclusionReason(projectEvidence, workType) {
|
|
264
|
+
if (!projectEvidence) return "NO_NODEJS_PROJECT_EVIDENCE";
|
|
265
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_NODEJS_SCOPE_MATCH";
|
|
266
|
+
if (!projectEvidence.frameworks.includes("nodejs")) return "NO_NODEJS_PRIMARY_EVIDENCE";
|
|
267
|
+
if (!hasNodeJsWorkContext(workType)) return "NO_NODEJS_EXECUTABLE_WORK";
|
|
268
|
+
return "NO_NODEJS_PRIMARY_EVIDENCE";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function rustExclusionReason(projectEvidence, workType) {
|
|
272
|
+
if (!projectEvidence) return "NO_RUST_PROJECT_EVIDENCE";
|
|
273
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_RUST_SCOPE_MATCH";
|
|
274
|
+
if (!projectEvidence.frameworks.includes("rust")) return "NO_RUST_PRIMARY_EVIDENCE";
|
|
275
|
+
if (!hasRustWorkContext(workType)) return "NO_RUST_EXECUTABLE_WORK";
|
|
276
|
+
return "NO_RUST_PRIMARY_EVIDENCE";
|
|
277
|
+
}
|
|
278
|
+
|
|
149
279
|
function exclusionReasonForGuide(guide, projectEvidence, workType) {
|
|
150
280
|
if (guide === "security") return "NO_TRUST_BOUNDARY";
|
|
151
281
|
if (guide === "performance") return "NO_MEASURABLE_PERFORMANCE_RISK";
|
|
@@ -154,6 +284,16 @@ function exclusionReasonForGuide(guide, projectEvidence, workType) {
|
|
|
154
284
|
if (guide === "taste") return "NO_TASTE_FRONTEND_CONTEXT";
|
|
155
285
|
if (guide === "documentation") return "NO_DOCUMENTATION_SURFACE";
|
|
156
286
|
if (guide === "flutter") return flutterExclusionReason(projectEvidence, workType);
|
|
287
|
+
if (guide === "dotnet") return dotNetExclusionReason(projectEvidence, workType);
|
|
288
|
+
if (guide === "nodejs") return nodeJsExclusionReason(projectEvidence, workType);
|
|
289
|
+
if (guide === "rust") return rustExclusionReason(projectEvidence, workType);
|
|
290
|
+
if (LANGUAGE_PROJECT_GUIDES.some(([language]) => language === guide)) {
|
|
291
|
+
if (!projectEvidence) return `NO_${guide.toUpperCase()}_PROJECT_EVIDENCE`;
|
|
292
|
+
if (projectEvidence.scope === "NO_MATCH") return `NO_${guide.toUpperCase()}_SCOPE_MATCH`;
|
|
293
|
+
if (!projectEvidence.frameworks.includes(guide)) return `NO_${guide.toUpperCase()}_PRIMARY_EVIDENCE`;
|
|
294
|
+
if (!hasLanguageWorkContext(workType)) return `NO_${guide.toUpperCase()}_EXECUTABLE_WORK`;
|
|
295
|
+
return `NO_${guide.toUpperCase()}_PRIMARY_EVIDENCE`;
|
|
296
|
+
}
|
|
157
297
|
return "NO_BEHAVIOR_OR_EXECUTABLE_CHANGE";
|
|
158
298
|
}
|
|
159
299
|
|
|
@@ -173,6 +313,64 @@ function reasonForSignal(prefix, signal) {
|
|
|
173
313
|
return `${prefix}_${signal.toUpperCase().replaceAll("-", "_")}`;
|
|
174
314
|
}
|
|
175
315
|
|
|
316
|
+
const SECURITY_TRUST_BOUNDARY_RISKS = Object.freeze([
|
|
317
|
+
"untrusted-input", "personal-data", "secrets", "external-service", "publication",
|
|
318
|
+
]);
|
|
319
|
+
|
|
320
|
+
// Canonical deterministic safety floor. These are the route reason codes the
|
|
321
|
+
// router assigns when a trust-boundary signal selects the security guide. The
|
|
322
|
+
// semantic exclusion policy reads protection from this same source, so a
|
|
323
|
+
// mandatory safety guide can never drift out of protection (for example the
|
|
324
|
+
// previously missing external-service boundary).
|
|
325
|
+
const MANDATORY_SAFETY_REASONS = Object.freeze(new Set([
|
|
326
|
+
"SURFACE_AUTH",
|
|
327
|
+
...SECURITY_TRUST_BOUNDARY_RISKS.map((risk) => reasonForSignal("RISK", risk)),
|
|
328
|
+
]));
|
|
329
|
+
|
|
330
|
+
export function isMandatorySafetyReason(reason) {
|
|
331
|
+
return MANDATORY_SAFETY_REASONS.has(reason);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function isMandatorySafetyGuide(guide, reasons) {
|
|
335
|
+
return guide === "security"
|
|
336
|
+
&& Array.isArray(reasons)
|
|
337
|
+
&& reasons.some((reason) => isMandatorySafetyReason(reason));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function applySemanticGuideRecommendation(selected, excluded, recommendation) {
|
|
341
|
+
const guideDecision = recommendation?.guideRelevance
|
|
342
|
+
?? (recommendation?.rankedIds || recommendation?.excludedIds ? recommendation : null);
|
|
343
|
+
if (!guideDecision) return;
|
|
344
|
+
const excludedIds = new Set(Array.isArray(guideDecision.excludedIds) ? guideDecision.excludedIds : []);
|
|
345
|
+
const confidenceById = guideDecision.confidenceById ?? {};
|
|
346
|
+
for (const guide of [...selected.keys()]) {
|
|
347
|
+
const reasons = selected.get(guide);
|
|
348
|
+
if (isMandatorySafetyGuide(guide, reasons)) {
|
|
349
|
+
if (!reasons.includes("MANDATORY_SAFETY_GUIDE")) reasons.push("MANDATORY_SAFETY_GUIDE");
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (!excludedIds.has(guide)) {
|
|
353
|
+
if (!reasons.includes("JEV_RELEVANT")) reasons.push("JEV_RELEVANT");
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if ((confidenceById[guide] ?? 0) < 0.75) {
|
|
357
|
+
if (!reasons.includes("JEV_LOW_CONFIDENCE_RETAINED")) reasons.push("JEV_LOW_CONFIDENCE_RETAINED");
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
selected.delete(guide);
|
|
361
|
+
excluded[guide] = ["JEV_EXCLUDED"];
|
|
362
|
+
}
|
|
363
|
+
const ranked = Array.isArray(guideDecision.rankedIds) ? guideDecision.rankedIds : [];
|
|
364
|
+
const order = new Map(ranked.map((guide, index) => [guide, index]));
|
|
365
|
+
const entries = [...selected.entries()].sort((left, right) => {
|
|
366
|
+
const leftRank = order.has(left[0]) ? order.get(left[0]) : ranked.length + [...selected.keys()].indexOf(left[0]);
|
|
367
|
+
const rightRank = order.has(right[0]) ? order.get(right[0]) : ranked.length + [...selected.keys()].indexOf(right[0]);
|
|
368
|
+
return leftRank - rightRank;
|
|
369
|
+
});
|
|
370
|
+
selected.clear();
|
|
371
|
+
for (const [guide, reasons] of entries) selected.set(guide, reasons);
|
|
372
|
+
}
|
|
373
|
+
|
|
176
374
|
function normalizeArray(value, name, allowed) {
|
|
177
375
|
if (value === undefined) return [];
|
|
178
376
|
if (!Array.isArray(value)) throw new RouteInputError(`${name} must be an array`);
|
|
@@ -209,6 +407,14 @@ function normalizeProjectEvidence(value) {
|
|
|
209
407
|
throw new RouteInputError(`Unsupported projectEvidence schema version: ${value.schemaVersion}`);
|
|
210
408
|
}
|
|
211
409
|
const frameworks = normalizeArray(value.frameworks, "frameworks", PROJECT_FRAMEWORKS);
|
|
410
|
+
const hasDotnet = frameworks.includes("dotnet");
|
|
411
|
+
const invalidOverlays = frameworks.filter((framework) => framework === "aspnetcore" || framework === "abp");
|
|
412
|
+
if (!hasDotnet && invalidOverlays.length > 0) {
|
|
413
|
+
const noun = invalidOverlays.length === 1 ? "framework" : "frameworks";
|
|
414
|
+
const verb = invalidOverlays.length === 1 ? "requires" : "require";
|
|
415
|
+
const labels = invalidOverlays.map((framework) => `"${framework}"`).join(", ");
|
|
416
|
+
throw new RouteInputError(`projectEvidence ${noun} ${labels} ${verb} "dotnet"`);
|
|
417
|
+
}
|
|
212
418
|
const scope = value.scope ?? (frameworks.length > 0 ? "UNSCOPED" : "NONE");
|
|
213
419
|
if (!PROJECT_EVIDENCE_SCOPES.includes(scope)) {
|
|
214
420
|
throw new RouteInputError(`Unknown project evidence scope: ${scope}`);
|
|
@@ -264,6 +470,11 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
264
470
|
|
|
265
471
|
const projectEvidence = normalized.projectEvidence;
|
|
266
472
|
addFlutterProjectGuides(normalized, add);
|
|
473
|
+
addDotNetProjectGuides(normalized, add);
|
|
474
|
+
addNodeJsProjectGuides(normalized, add);
|
|
475
|
+
addRustProjectGuides(normalized, add);
|
|
476
|
+
addLanguageProjectGuides(normalized, add);
|
|
477
|
+
addProjectBaselineGuides(normalized, add);
|
|
267
478
|
|
|
268
479
|
const workReason = reasonForWorkType(normalized.workType);
|
|
269
480
|
for (const guide of WORK_GUIDES[normalized.workType]) add(guide, workReason);
|
|
@@ -287,7 +498,7 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
287
498
|
}
|
|
288
499
|
|
|
289
500
|
for (const risk of normalized.risks) {
|
|
290
|
-
if (
|
|
501
|
+
if (SECURITY_TRUST_BOUNDARY_RISKS.includes(risk)) {
|
|
291
502
|
add("security", reasonForSignal("RISK", risk));
|
|
292
503
|
}
|
|
293
504
|
if (["critical-path", "performance"].includes(risk)) {
|
|
@@ -328,12 +539,19 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
328
539
|
excluded[guide] = [exclusionReasonForGuide(guide, projectEvidence, normalized.workType)];
|
|
329
540
|
}
|
|
330
541
|
|
|
542
|
+
applySemanticGuideRecommendation(
|
|
543
|
+
selected,
|
|
544
|
+
excluded,
|
|
545
|
+
profileOptions.semanticRecommendation ?? null,
|
|
546
|
+
);
|
|
547
|
+
|
|
331
548
|
const guides = [...selected.keys()];
|
|
332
549
|
const result = {
|
|
333
550
|
schemaVersion: ROUTING_SCHEMA_VERSION,
|
|
334
551
|
protocolVersion: PROTOCOL_VERSION,
|
|
335
552
|
input: normalized,
|
|
336
553
|
primary: Object.prototype.hasOwnProperty.call(PRIMARY_GUIDES, normalized.workType)
|
|
554
|
+
&& guides.includes(PRIMARY_GUIDES[normalized.workType])
|
|
337
555
|
? PRIMARY_GUIDES[normalized.workType]
|
|
338
556
|
: guides[0] ?? null,
|
|
339
557
|
guides,
|
|
@@ -346,6 +564,7 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
346
564
|
configuredProfile: profileOptions.configuredProfile ?? input.configuredProfile ?? "auto",
|
|
347
565
|
requestedProfile: profileOptions.requestedProfile
|
|
348
566
|
?? (Object.prototype.hasOwnProperty.call(input, "executionProfile") ? input.executionProfile : null),
|
|
567
|
+
semanticRecommendation: profileOptions.semanticRecommendation ?? null,
|
|
349
568
|
}),
|
|
350
569
|
};
|
|
351
570
|
return assertRouteInvariants(result);
|
|
@@ -4,8 +4,22 @@ import {
|
|
|
4
4
|
normalizeVerificationExecutionPolicy,
|
|
5
5
|
} from "./verification-execution.js";
|
|
6
6
|
import { STRUCTURAL_QUALITY_PROVIDER_ID_PATTERN } from "./structural-quality/constants.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
E_ADVISORY_CONTEXT_PROVIDER_INVALID,
|
|
9
|
+
E_BROWSER_VERIFICATION_PROVIDER_INVALID,
|
|
10
|
+
E_SECURITY_REVIEW_PROVIDER_INVALID,
|
|
11
|
+
} from "./error-codes.js";
|
|
8
12
|
import { assertAdvisoryContextProviderIdentity } from "./advisory-context/provider.js";
|
|
13
|
+
import {
|
|
14
|
+
BROWSER_VERIFICATION_PROVIDER_ID_PATTERN,
|
|
15
|
+
assertBrowserVerificationProvider,
|
|
16
|
+
assertBrowserVerificationProviderIdentity,
|
|
17
|
+
} from "./browser-verification/provider.js";
|
|
18
|
+
import {
|
|
19
|
+
SECURITY_REVIEW_PROVIDER_ID_PATTERN,
|
|
20
|
+
assertSecurityReviewProvider,
|
|
21
|
+
assertSecurityReviewProviderIdentity,
|
|
22
|
+
} from "./security-review/provider.js";
|
|
9
23
|
|
|
10
24
|
export const AUTHORITY_TRUST_MODES = Object.freeze(["NONE", "HOST_ATTESTED"]);
|
|
11
25
|
|
|
@@ -83,6 +97,100 @@ export function resolveAuthorityContext(options = {}) {
|
|
|
83
97
|
return hasDirectAuthority ? createAuthorityContext({ ...source, trustMode: "NONE" }) : createAuthorityContext();
|
|
84
98
|
}
|
|
85
99
|
|
|
100
|
+
function isProviderObject(value) {
|
|
101
|
+
return value && typeof value === "object" && !Array.isArray(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function providerEntries(configured, label, code) {
|
|
105
|
+
if (!configured || typeof configured !== "object" || Array.isArray(configured)) {
|
|
106
|
+
const error = new Error(`${label} must be an object or Map`);
|
|
107
|
+
error.code = code;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
return configured instanceof Map ? [...configured.entries()] : Object.entries(configured);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function registerProviders(configured, { label, code, idError, validateId, providerError, validateProvider }) {
|
|
114
|
+
const providers = {};
|
|
115
|
+
for (const [id, provider] of providerEntries(configured, label, code)) {
|
|
116
|
+
if (!validateId(id)) {
|
|
117
|
+
const error = new Error(idError(id));
|
|
118
|
+
error.code = code;
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
if (typeof provider !== "function" && !isProviderObject(provider)) {
|
|
122
|
+
const error = new Error(providerError(id));
|
|
123
|
+
error.code = code;
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
if (typeof provider !== "function") validateProvider(provider, id);
|
|
127
|
+
providers[id] = provider;
|
|
128
|
+
}
|
|
129
|
+
return Object.freeze(providers);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function configureUsageProvider(context, provider) {
|
|
133
|
+
if (!provider
|
|
134
|
+
|| typeof provider !== "object"
|
|
135
|
+
|| Array.isArray(provider)
|
|
136
|
+
|| typeof provider.getTaskUsage !== "function") {
|
|
137
|
+
const error = new Error("Usage provider must expose getTaskUsage({ projectPath, taskId })");
|
|
138
|
+
error.code = "E_USAGE_INVALID";
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
context.usageProvider = provider;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function configureStructuralQualityProviders(context, configured) {
|
|
145
|
+
context.structuralQualityProviders = registerProviders(configured, {
|
|
146
|
+
label: "structuralQualityProviders",
|
|
147
|
+
code: "E_STRUCTURAL_QUALITY_PROVIDER_INVALID",
|
|
148
|
+
idError: id => `Invalid or reserved structural-quality provider ID: ${id}`,
|
|
149
|
+
validateId: id => STRUCTURAL_QUALITY_PROVIDER_ID_PATTERN.test(id) && id !== "sentrux",
|
|
150
|
+
providerError: id => `Structural-quality provider ${id} must be an object or factory`,
|
|
151
|
+
validateProvider: (provider, id) => {},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function configureAdvisoryContextProviders(context, configured) {
|
|
156
|
+
context.advisoryContextProviders = registerProviders(configured, {
|
|
157
|
+
label: "advisoryContextProviders",
|
|
158
|
+
code: E_ADVISORY_CONTEXT_PROVIDER_INVALID,
|
|
159
|
+
idError: id => `Invalid advisory-context provider ID: ${id}`,
|
|
160
|
+
validateId: id => /^[a-z0-9][a-z0-9_-]*$/.test(id),
|
|
161
|
+
providerError: id => `Advisory-context provider ${id} must be an object or factory`,
|
|
162
|
+
validateProvider: assertAdvisoryContextProviderIdentity,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function configureBrowserVerificationProviders(context, configured) {
|
|
167
|
+
context.browserVerificationProviders = registerProviders(configured, {
|
|
168
|
+
label: "browserVerificationProviders",
|
|
169
|
+
code: E_BROWSER_VERIFICATION_PROVIDER_INVALID,
|
|
170
|
+
idError: id => `Invalid browser-verification provider ID: ${id}`,
|
|
171
|
+
validateId: id => BROWSER_VERIFICATION_PROVIDER_ID_PATTERN.test(id),
|
|
172
|
+
providerError: id => `Browser-verification provider ${id} must be an object with verify() or a factory`,
|
|
173
|
+
validateProvider: (provider, id) => {
|
|
174
|
+
assertBrowserVerificationProviderIdentity(provider, { expectedId: id });
|
|
175
|
+
assertBrowserVerificationProvider(provider, { label: `browser-verification provider "${id}"` });
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function configureSecurityReviewProviders(context, configured) {
|
|
181
|
+
context.securityReviewProviders = registerProviders(configured, {
|
|
182
|
+
label: "securityReviewProviders",
|
|
183
|
+
code: E_SECURITY_REVIEW_PROVIDER_INVALID,
|
|
184
|
+
idError: id => `Invalid security-review provider ID: ${id}`,
|
|
185
|
+
validateId: id => SECURITY_REVIEW_PROVIDER_ID_PATTERN.test(id),
|
|
186
|
+
providerError: id => `Security-review provider ${id} must be an object with review() or a factory`,
|
|
187
|
+
validateProvider: (provider, id) => {
|
|
188
|
+
assertSecurityReviewProviderIdentity(provider, { expectedId: id });
|
|
189
|
+
assertSecurityReviewProvider(provider, { label: `security-review provider "${id}"` });
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
86
194
|
export function createForgeLoopContext(options = {}) {
|
|
87
195
|
const authorityContext = createAuthorityContext(options);
|
|
88
196
|
const context = { authorityContext };
|
|
@@ -98,70 +206,19 @@ export function createForgeLoopContext(options = {}) {
|
|
|
98
206
|
context.verificationExecutionPolicy = normalizeVerificationExecutionPolicy(options.verificationExecutionPolicy);
|
|
99
207
|
}
|
|
100
208
|
if (options?.usageProvider !== undefined) {
|
|
101
|
-
|
|
102
|
-
|| typeof options.usageProvider !== "object"
|
|
103
|
-
|| Array.isArray(options.usageProvider)
|
|
104
|
-
|| typeof options.usageProvider.getTaskUsage !== "function") {
|
|
105
|
-
const error = new Error("Usage provider must expose getTaskUsage({ projectPath, taskId })");
|
|
106
|
-
error.code = "E_USAGE_INVALID";
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
context.usageProvider = options.usageProvider;
|
|
209
|
+
configureUsageProvider(context, options.usageProvider);
|
|
110
210
|
}
|
|
111
211
|
if (options?.structuralQualityProviders !== undefined) {
|
|
112
|
-
|
|
113
|
-
? Object.fromEntries(options.structuralQualityProviders.entries())
|
|
114
|
-
: options.structuralQualityProviders;
|
|
115
|
-
if (!configured || typeof configured !== "object" || Array.isArray(configured)) {
|
|
116
|
-
const error = new Error("structuralQualityProviders must be an object or Map");
|
|
117
|
-
error.code = "E_STRUCTURAL_QUALITY_PROVIDER_INVALID";
|
|
118
|
-
throw error;
|
|
119
|
-
}
|
|
120
|
-
const providers = {};
|
|
121
|
-
for (const [id, provider] of Object.entries(configured)) {
|
|
122
|
-
if (!STRUCTURAL_QUALITY_PROVIDER_ID_PATTERN.test(id) || id === "sentrux") {
|
|
123
|
-
const error = new Error(`Invalid or reserved structural-quality provider ID: ${id}`);
|
|
124
|
-
error.code = "E_STRUCTURAL_QUALITY_PROVIDER_INVALID";
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
if (typeof provider !== "function"
|
|
128
|
-
&& (!provider || typeof provider !== "object" || Array.isArray(provider))) {
|
|
129
|
-
const error = new Error(`Structural-quality provider ${id} must be an object or factory`);
|
|
130
|
-
error.code = "E_STRUCTURAL_QUALITY_PROVIDER_INVALID";
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
providers[id] = provider;
|
|
134
|
-
}
|
|
135
|
-
context.structuralQualityProviders = Object.freeze(providers);
|
|
212
|
+
configureStructuralQualityProviders(context, options.structuralQualityProviders);
|
|
136
213
|
}
|
|
137
214
|
if (options?.advisoryContextProviders !== undefined) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
const providers = {};
|
|
147
|
-
for (const [id, provider] of Object.entries(configured)) {
|
|
148
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/.test(id)) {
|
|
149
|
-
const error = new Error(`Invalid advisory-context provider ID: ${id}`);
|
|
150
|
-
error.code = E_ADVISORY_CONTEXT_PROVIDER_INVALID;
|
|
151
|
-
throw error;
|
|
152
|
-
}
|
|
153
|
-
if (typeof provider !== "function"
|
|
154
|
-
&& (!provider || typeof provider !== "object" || Array.isArray(provider))) {
|
|
155
|
-
const error = new Error(`Advisory-context provider ${id} must be an object or factory`);
|
|
156
|
-
error.code = E_ADVISORY_CONTEXT_PROVIDER_INVALID;
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
if (typeof provider !== "function") {
|
|
160
|
-
assertAdvisoryContextProviderIdentity(provider, id);
|
|
161
|
-
}
|
|
162
|
-
providers[id] = provider;
|
|
163
|
-
}
|
|
164
|
-
context.advisoryContextProviders = Object.freeze(providers);
|
|
215
|
+
configureAdvisoryContextProviders(context, options.advisoryContextProviders);
|
|
216
|
+
}
|
|
217
|
+
if (options?.browserVerificationProviders !== undefined) {
|
|
218
|
+
configureBrowserVerificationProviders(context, options.browserVerificationProviders);
|
|
219
|
+
}
|
|
220
|
+
if (options?.securityReviewProviders !== undefined) {
|
|
221
|
+
configureSecurityReviewProviders(context, options.securityReviewProviders);
|
|
165
222
|
}
|
|
166
223
|
return Object.freeze(context);
|
|
167
224
|
}
|