sentinelayer-cli 0.26.0 → 0.27.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/package.json
CHANGED
package/src/ai/client.js
CHANGED
|
@@ -413,10 +413,18 @@ export class MultiProviderApiClient {
|
|
|
413
413
|
* prompt?: string,
|
|
414
414
|
* stream?: boolean,
|
|
415
415
|
* apiKey?: string,
|
|
416
|
+
* apiUrl?: string,
|
|
416
417
|
* env?: NodeJS.ProcessEnv,
|
|
417
|
-
* onChunk?: (chunk: string) => void
|
|
418
|
+
* onChunk?: (chunk: string) => void,
|
|
419
|
+
* sessionId?: string,
|
|
420
|
+
* agentId?: string,
|
|
421
|
+
* action?: string,
|
|
422
|
+
* usageIdempotencyKey?: string,
|
|
423
|
+
* billingTier?: string,
|
|
424
|
+
* customerPricingPolicy?: string,
|
|
425
|
+
* metadata?: object
|
|
418
426
|
* }} [options]
|
|
419
|
-
* @returns {Promise<{ provider: string, model: string, text: string }>}
|
|
427
|
+
* @returns {Promise<{ provider: string, model: string, text: string, usage?: object, usageLedger?: object | null }>}
|
|
420
428
|
*/
|
|
421
429
|
async invoke({
|
|
422
430
|
provider,
|
|
@@ -424,8 +432,16 @@ export class MultiProviderApiClient {
|
|
|
424
432
|
prompt,
|
|
425
433
|
stream = false,
|
|
426
434
|
apiKey,
|
|
435
|
+
apiUrl = "",
|
|
427
436
|
env = process.env,
|
|
428
437
|
onChunk,
|
|
438
|
+
sessionId = "",
|
|
439
|
+
agentId = "",
|
|
440
|
+
action = "",
|
|
441
|
+
usageIdempotencyKey = "",
|
|
442
|
+
billingTier = "",
|
|
443
|
+
customerPricingPolicy = "",
|
|
444
|
+
metadata = null,
|
|
429
445
|
} = {}) {
|
|
430
446
|
const resolvedProvider = resolveProvider({ provider, env });
|
|
431
447
|
const resolvedModel = resolveModel({ provider: resolvedProvider, model });
|
|
@@ -437,10 +453,21 @@ export class MultiProviderApiClient {
|
|
|
437
453
|
systemPrompt: "",
|
|
438
454
|
model: resolvedModel,
|
|
439
455
|
maxTokens: 4096,
|
|
456
|
+
apiUrl,
|
|
457
|
+
token: apiKey,
|
|
458
|
+
sessionId,
|
|
459
|
+
agentId,
|
|
460
|
+
action,
|
|
461
|
+
usageIdempotencyKey,
|
|
462
|
+
billingTier,
|
|
463
|
+
customerPricingPolicy,
|
|
464
|
+
metadata,
|
|
465
|
+
fetchImpl: this.fetchImpl,
|
|
440
466
|
});
|
|
441
467
|
return {
|
|
442
468
|
text: result.text,
|
|
443
469
|
usage: result.usage,
|
|
470
|
+
usageLedger: result.usageLedger,
|
|
444
471
|
provider: "sentinelayer",
|
|
445
472
|
model: resolvedModel,
|
|
446
473
|
};
|
|
@@ -58,6 +58,9 @@ export function buildLegacyArgs(baseArgs, { commandOptions = {}, command } = {})
|
|
|
58
58
|
appendPassthroughFlag(args, "--model", commandOptions.model);
|
|
59
59
|
appendPassthroughFlag(args, "--provider", commandOptions.provider);
|
|
60
60
|
appendPassthroughFlag(args, "--reuse-omargate", commandOptions.reuseOmargate);
|
|
61
|
+
appendPassthroughFlag(args, "--scope-mode", commandOptions.scopeMode);
|
|
62
|
+
appendBooleanFlag(args, "--diff", commandOptions.diff);
|
|
63
|
+
appendBooleanFlag(args, "--staged", commandOptions.staged);
|
|
61
64
|
appendPassthroughFlag(args, "--notify-email", commandOptions.notifyEmail);
|
|
62
65
|
appendPassthroughFlag(args, "--email-on-complete", commandOptions.emailOnComplete);
|
|
63
66
|
appendPassthroughFlag(args, "--notify-session", commandOptions.notifySession);
|
package/src/commands/omargate.js
CHANGED
|
@@ -12,6 +12,9 @@ export function registerOmarGateCommand(program, invokeLegacy) {
|
|
|
12
12
|
.option("--output-dir <path>", "Artifact root for report output")
|
|
13
13
|
.option("--no-ai", "Skip AI review layer (deterministic only)")
|
|
14
14
|
.option("--ai-dry-run", "Run AI layer in dry-run mode (no LLM call)")
|
|
15
|
+
.option("--scope-mode <mode>", "Deterministic file scope: full, diff, or staged")
|
|
16
|
+
.option("--diff", "Alias for --scope-mode diff")
|
|
17
|
+
.option("--staged", "Alias for --scope-mode staged")
|
|
15
18
|
.option("--scan-mode <mode>", "Scan depth: baseline (1 persona), deep (13), full-depth (13)")
|
|
16
19
|
.option("--max-parallel <n>", "Max concurrent persona calls (default: 4)")
|
|
17
20
|
.option("--model <id>", "LLM model override (default: gpt-5.3-codex)")
|
package/src/legacy-cli.js
CHANGED
|
@@ -847,6 +847,33 @@ function hasCommandOption(args, optionName) {
|
|
|
847
847
|
return args.some((arg) => String(arg || "").trim() === optionName);
|
|
848
848
|
}
|
|
849
849
|
|
|
850
|
+
function resolveScopeModeFromOptions(args, { defaultMode = "full" } = {}) {
|
|
851
|
+
const explicitMode = String(getCommandOptionValue(args, "--scope-mode") || "").trim().toLowerCase();
|
|
852
|
+
const diffFlag = hasCommandOption(args, "--diff");
|
|
853
|
+
const stagedFlag = hasCommandOption(args, "--staged");
|
|
854
|
+
|
|
855
|
+
if (diffFlag && stagedFlag) {
|
|
856
|
+
throw new Error("Use only one of --diff or --staged.");
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
let modeFromFlags = "";
|
|
860
|
+
if (diffFlag) {
|
|
861
|
+
modeFromFlags = "diff";
|
|
862
|
+
} else if (stagedFlag) {
|
|
863
|
+
modeFromFlags = "staged";
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (explicitMode && modeFromFlags && explicitMode !== modeFromFlags) {
|
|
867
|
+
throw new Error(`Conflicting scope selection: --scope-mode ${explicitMode} with --${modeFromFlags}.`);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const resolved = explicitMode || modeFromFlags || defaultMode;
|
|
871
|
+
if (!["full", "diff", "staged"].includes(resolved)) {
|
|
872
|
+
throw new Error("scope mode must be one of: full, diff, staged.");
|
|
873
|
+
}
|
|
874
|
+
return resolved;
|
|
875
|
+
}
|
|
876
|
+
|
|
850
877
|
async function collectScanFiles(rootPath) {
|
|
851
878
|
const files = [];
|
|
852
879
|
const stack = [rootPath];
|
|
@@ -1237,6 +1264,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1237
1264
|
const asJson = hasCommandOption(args, "--json");
|
|
1238
1265
|
const pathArg = getCommandOptionValue(args, "--path") || ".";
|
|
1239
1266
|
const outputDirArg = getCommandOptionValue(args, "--output-dir") || "";
|
|
1267
|
+
const scopeMode = resolveScopeModeFromOptions(args, { defaultMode: "full" });
|
|
1240
1268
|
const aiEnabled = !hasCommandOption(args, "--no-ai");
|
|
1241
1269
|
const aiDryRun = hasCommandOption(args, "--ai-dry-run");
|
|
1242
1270
|
const maxCostUsd = parseFloat(getCommandOptionValue(args, "--max-cost") || "5.0") || 5.0;
|
|
@@ -1261,6 +1289,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1261
1289
|
if (!asJson) {
|
|
1262
1290
|
printSection("Local Omar Gate Deep");
|
|
1263
1291
|
printInfo(`Target: ${targetPath}`);
|
|
1292
|
+
printInfo(`Scope mode: ${scopeMode}`);
|
|
1264
1293
|
printInfo(`Scan mode: ${scanMode} | AI: ${aiEnabled ? "enabled" : "disabled"}`);
|
|
1265
1294
|
console.error("");
|
|
1266
1295
|
console.error(pc.gray(` [${formatElapsed(Date.now() - commandStartedAt)}] Phase 1: Deterministic analysis (22 rules)...`));
|
|
@@ -1270,13 +1299,17 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1270
1299
|
const { runDeterministicReviewPipeline } = await import("./review/local-review.js");
|
|
1271
1300
|
const deterministic = await runDeterministicReviewPipeline({
|
|
1272
1301
|
targetPath,
|
|
1273
|
-
mode:
|
|
1302
|
+
mode: scopeMode,
|
|
1274
1303
|
outputDir: outputDirArg,
|
|
1275
1304
|
});
|
|
1276
1305
|
|
|
1277
1306
|
const detFindings = deterministic.findings || [];
|
|
1278
1307
|
const detSummary = deterministic.summary || { P0: 0, P1: 0, P2: 0, P3: 0, blocking: false };
|
|
1279
|
-
const scannedFiles =
|
|
1308
|
+
const scannedFiles =
|
|
1309
|
+
deterministic.scope?.scannedFiles ??
|
|
1310
|
+
deterministic.metadata?.ingest?.filesScanned ??
|
|
1311
|
+
deterministic.metadata?.scannedFiles ??
|
|
1312
|
+
detFindings.length;
|
|
1280
1313
|
|
|
1281
1314
|
if (!asJson) {
|
|
1282
1315
|
console.error(` ${pc.green("✓")} [${formatElapsed(Date.now() - commandStartedAt)}] Deterministic: ${scannedFiles} files → P1=${detSummary.P1} P2=${detSummary.P2} findings`);
|
|
@@ -1312,6 +1345,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1312
1345
|
deterministic: {
|
|
1313
1346
|
summary: detSummary,
|
|
1314
1347
|
findings: detFindings,
|
|
1348
|
+
mode: deterministic.mode || scopeMode,
|
|
1315
1349
|
scope: deterministic.scope || {},
|
|
1316
1350
|
layers: deterministic.layers || {},
|
|
1317
1351
|
metadata: deterministic.metadata || {},
|
|
@@ -1430,6 +1464,8 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1430
1464
|
runId: orchestratorResult.runId,
|
|
1431
1465
|
mode: orchestratorResult.mode,
|
|
1432
1466
|
roster: orchestratorResult.roster,
|
|
1467
|
+
basePersonas: orchestratorResult.basePersonas || [],
|
|
1468
|
+
personaRouting: orchestratorResult.personaRouting || null,
|
|
1433
1469
|
findings: (orchestratorResult.personas || []).flatMap((p) => []),
|
|
1434
1470
|
aiFindings: aiFindings,
|
|
1435
1471
|
personaCount: (orchestratorResult.personas || []).length,
|
|
@@ -1445,6 +1481,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1445
1481
|
findings: orchestratorResult.findings || [],
|
|
1446
1482
|
summary: orchestratorResult.summary,
|
|
1447
1483
|
reconciliation: orchestratorResult.reconciliation,
|
|
1484
|
+
personaRouting: orchestratorResult.personaRouting || null,
|
|
1448
1485
|
findingsBySource: orchestratorResult.findingsBySource,
|
|
1449
1486
|
});
|
|
1450
1487
|
}
|
|
@@ -1461,6 +1498,8 @@ Elapsed: ${totalElapsed}
|
|
|
1461
1498
|
|
|
1462
1499
|
Summary:
|
|
1463
1500
|
- Files scanned: ${scannedFiles}
|
|
1501
|
+
- Scope mode: ${scopeMode}
|
|
1502
|
+
- Persona routing: ${orchestratorResult?.personaRouting?.enabled ? `${orchestratorResult.personaRouting.effectivePersonas.length}/${orchestratorResult.personaRouting.basePersonas.length} personas for ${orchestratorResult.personaRouting.changedFileCount} changed files` : "full roster"}
|
|
1464
1503
|
- Deterministic findings: P0=${detSummary.P0} P1=${detSummary.P1} P2=${detSummary.P2} P3=${detSummary.P3 || 0}
|
|
1465
1504
|
- AI findings (raw, pre-reconciliation): ${aiResult ? `P0=${aiResult.summary?.P0 || 0} P1=${aiResult.summary?.P1 || 0} P2=${aiResult.summary?.P2 || 0} P3=${aiResult.summary?.P3 || 0}` : "skipped"}
|
|
1466
1505
|
- Reconciled (deduped + confidence-boosted): P0=${combinedP0} P1=${combinedP1} P2=${combinedP2} P3=${combinedP3}
|
|
@@ -1494,6 +1533,8 @@ ${formatFindingsMarkdown(allFindings)}
|
|
|
1494
1533
|
targetPath,
|
|
1495
1534
|
runId: omargateRunId,
|
|
1496
1535
|
reportPath,
|
|
1536
|
+
scopeMode,
|
|
1537
|
+
scopedFiles: deterministic.scope?.scannedRelativeFiles || [],
|
|
1497
1538
|
scannedFiles,
|
|
1498
1539
|
p0: combinedP0,
|
|
1499
1540
|
p1: combinedP1,
|
|
@@ -1518,6 +1559,8 @@ ${formatFindingsMarkdown(allFindings)}
|
|
|
1518
1559
|
}
|
|
1519
1560
|
: null,
|
|
1520
1561
|
reconciliation: orchestratorResult?.reconciliation || null,
|
|
1562
|
+
personaRouting: orchestratorResult?.personaRouting || null,
|
|
1563
|
+
basePersonas: orchestratorResult?.basePersonas || [],
|
|
1521
1564
|
roster: orchestratorResult?.roster || [],
|
|
1522
1565
|
},
|
|
1523
1566
|
null,
|
|
@@ -15,6 +15,7 @@ import { reconcileReviewFindings } from "./report.js";
|
|
|
15
15
|
import { resolvePersonaVisual } from "../agents/persona-visuals.js";
|
|
16
16
|
import { syncRunToDashboard } from "../telemetry/sync.js";
|
|
17
17
|
import { createAgentEvent } from "../events/schema.js";
|
|
18
|
+
import { buildOwnershipMap, computeRoutingStats, loadScaffoldConfig } from "../ingest/ownership.js";
|
|
18
19
|
|
|
19
20
|
const OMAR_ORCHESTRATOR_AGENT = Object.freeze({
|
|
20
21
|
id: "omar-orchestrator",
|
|
@@ -98,6 +99,158 @@ function uniqueScopeFiles(files = []) {
|
|
|
98
99
|
return normalized;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
function normalizeScopeMode(value) {
|
|
103
|
+
const normalized = normalizeString(value).toLowerCase();
|
|
104
|
+
if (normalized === "diff" || normalized === "staged") {
|
|
105
|
+
return normalized;
|
|
106
|
+
}
|
|
107
|
+
return "full";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function findingRelativePath(finding = {}) {
|
|
111
|
+
return toPosixPath(finding.file || finding.path || finding.location || "");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function summarizeDeterministicFindings(findings = []) {
|
|
115
|
+
return summarizeFindings(findings);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildScopeForFiles(scope = {}, files = []) {
|
|
119
|
+
const wanted = new Set(files.map((file) => toPosixPath(file)));
|
|
120
|
+
const scopedObjects = filesFromScope(scope).filter((file) => wanted.has(file.path));
|
|
121
|
+
const scopedFiles =
|
|
122
|
+
scopedObjects.length > 0
|
|
123
|
+
? scopedObjects
|
|
124
|
+
: files.map((filePath) => ({ path: toPosixPath(filePath), loc: 0 }));
|
|
125
|
+
const totalLoc = scopedFiles.reduce((sum, file) => sum + (file.loc > 0 ? file.loc : 80), 0);
|
|
126
|
+
return {
|
|
127
|
+
...scope,
|
|
128
|
+
files: scopedFiles,
|
|
129
|
+
primary: scopedFiles,
|
|
130
|
+
scannedFiles: files.length,
|
|
131
|
+
scannedRelativeFiles: files,
|
|
132
|
+
totalLoc,
|
|
133
|
+
estimatedLoc: totalLoc,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildPersonaScopedDeterministic(deterministic = {}, personaId, personaRouting = {}) {
|
|
138
|
+
if (!personaRouting?.enabled) {
|
|
139
|
+
return deterministic;
|
|
140
|
+
}
|
|
141
|
+
const files = Array.isArray(personaRouting.filesByPersona?.[personaId])
|
|
142
|
+
? personaRouting.filesByPersona[personaId]
|
|
143
|
+
: [];
|
|
144
|
+
const wanted = new Set(files);
|
|
145
|
+
const findings = (deterministic?.findings || []).filter((finding) =>
|
|
146
|
+
wanted.has(findingRelativePath(finding))
|
|
147
|
+
);
|
|
148
|
+
const summary = summarizeDeterministicFindings(findings);
|
|
149
|
+
return {
|
|
150
|
+
...deterministic,
|
|
151
|
+
summary,
|
|
152
|
+
findings,
|
|
153
|
+
scope: buildScopeForFiles(deterministic?.scope || {}, files),
|
|
154
|
+
metadata: {
|
|
155
|
+
...(deterministic?.metadata || {}),
|
|
156
|
+
personaRouting: {
|
|
157
|
+
enabled: true,
|
|
158
|
+
scopeMode: personaRouting.scopeMode,
|
|
159
|
+
personaId,
|
|
160
|
+
scopedFiles: files.length,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function resolveChangedFilePersonaRouting({
|
|
167
|
+
targetPath,
|
|
168
|
+
personas = [],
|
|
169
|
+
deterministic = {},
|
|
170
|
+
manualInclude = false,
|
|
171
|
+
} = {}) {
|
|
172
|
+
const scopeMode = normalizeScopeMode(deterministic?.mode || deterministic?.metadata?.scopeMode);
|
|
173
|
+
const basePersonas = [...personas];
|
|
174
|
+
const base = {
|
|
175
|
+
enabled: false,
|
|
176
|
+
scopeMode,
|
|
177
|
+
reason: "full_scope",
|
|
178
|
+
basePersonas,
|
|
179
|
+
effectivePersonas: basePersonas,
|
|
180
|
+
droppedPersonas: [],
|
|
181
|
+
changedFileCount: 0,
|
|
182
|
+
personaCoverage: {},
|
|
183
|
+
tokenReductionEstimatePct: 0,
|
|
184
|
+
filesByPersona: {},
|
|
185
|
+
scaffold: { found: false },
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (scopeMode === "full") {
|
|
189
|
+
return base;
|
|
190
|
+
}
|
|
191
|
+
if (manualInclude) {
|
|
192
|
+
return {
|
|
193
|
+
...base,
|
|
194
|
+
reason: "manual_persona_filter",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const scopedFiles = filesFromScope(deterministic?.scope || {})
|
|
199
|
+
.map((file) => file.path)
|
|
200
|
+
.filter(Boolean);
|
|
201
|
+
if (scopedFiles.length === 0) {
|
|
202
|
+
return {
|
|
203
|
+
...base,
|
|
204
|
+
enabled: true,
|
|
205
|
+
reason: "no_changed_files",
|
|
206
|
+
effectivePersonas: [],
|
|
207
|
+
droppedPersonas: basePersonas,
|
|
208
|
+
changedFileCount: 0,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let scaffold = { found: false, ownershipRules: [] };
|
|
213
|
+
try {
|
|
214
|
+
scaffold = await loadScaffoldConfig({ targetPath });
|
|
215
|
+
} catch {
|
|
216
|
+
scaffold = { found: false, ownershipRules: [] };
|
|
217
|
+
}
|
|
218
|
+
const ownershipMap = buildOwnershipMap(scopedFiles, scaffold);
|
|
219
|
+
const stats = computeRoutingStats(ownershipMap);
|
|
220
|
+
const allowed = new Set(basePersonas);
|
|
221
|
+
const filesByPersona = {};
|
|
222
|
+
for (const [file, persona] of ownershipMap.entries()) {
|
|
223
|
+
if (!allowed.has(persona)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (!filesByPersona[persona]) {
|
|
227
|
+
filesByPersona[persona] = [];
|
|
228
|
+
}
|
|
229
|
+
filesByPersona[persona].push(file);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const persona of Object.keys(filesByPersona)) {
|
|
233
|
+
filesByPersona[persona].sort((left, right) => left.localeCompare(right));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const effectivePersonas = basePersonas.filter((persona) => filesByPersona[persona]?.length > 0);
|
|
237
|
+
return {
|
|
238
|
+
...base,
|
|
239
|
+
enabled: true,
|
|
240
|
+
reason: "changed_file_scope",
|
|
241
|
+
effectivePersonas,
|
|
242
|
+
droppedPersonas: basePersonas.filter((persona) => !effectivePersonas.includes(persona)),
|
|
243
|
+
changedFileCount: scopedFiles.length,
|
|
244
|
+
personaCoverage: stats.personaCoverage,
|
|
245
|
+
tokenReductionEstimatePct: stats.tokenReductionEstimatePct,
|
|
246
|
+
filesByPersona,
|
|
247
|
+
scaffold: {
|
|
248
|
+
found: Boolean(scaffold?.found),
|
|
249
|
+
ruleCount: Array.isArray(scaffold?.ownershipRules) ? scaffold.ownershipRules.length : 0,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
101
254
|
function filesFromScope(scope = {}) {
|
|
102
255
|
if (Array.isArray(scope.files)) {
|
|
103
256
|
return uniqueScopeFiles(scope.files);
|
|
@@ -624,7 +777,7 @@ export async function runOmarGateOrchestrator({
|
|
|
624
777
|
})
|
|
625
778
|
: { ...resolveScanMode(scanMode), dropped: [], unknown: [] };
|
|
626
779
|
|
|
627
|
-
const { mode, personas } = resolved;
|
|
780
|
+
const { mode, personas: basePersonas } = resolved;
|
|
628
781
|
const droppedPersonas = resolved.dropped || [];
|
|
629
782
|
const unknownPersonas = resolved.unknown || [];
|
|
630
783
|
|
|
@@ -637,7 +790,28 @@ export async function runOmarGateOrchestrator({
|
|
|
637
790
|
mode,
|
|
638
791
|
dropped: droppedPersonas,
|
|
639
792
|
unknown: unknownPersonas,
|
|
640
|
-
effective:
|
|
793
|
+
effective: basePersonas,
|
|
794
|
+
},
|
|
795
|
+
runId,
|
|
796
|
+
}));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
const personaRouting = await resolveChangedFilePersonaRouting({
|
|
800
|
+
targetPath,
|
|
801
|
+
personas: basePersonas,
|
|
802
|
+
deterministic,
|
|
803
|
+
manualInclude: Array.isArray(includeOnly) && includeOnly.length > 0,
|
|
804
|
+
});
|
|
805
|
+
const personas = personaRouting.effectivePersonas;
|
|
806
|
+
|
|
807
|
+
if (onEvent && personaRouting.enabled) {
|
|
808
|
+
onEvent(createAgentEvent({
|
|
809
|
+
event: "omargate_persona_routing",
|
|
810
|
+
agent: OMAR_ORCHESTRATOR_AGENT,
|
|
811
|
+
payload: {
|
|
812
|
+
runId,
|
|
813
|
+
mode,
|
|
814
|
+
routing: personaRouting,
|
|
641
815
|
},
|
|
642
816
|
runId,
|
|
643
817
|
}));
|
|
@@ -659,7 +833,17 @@ export async function runOmarGateOrchestrator({
|
|
|
659
833
|
onEvent(createAgentEvent({
|
|
660
834
|
event: "omargate_start",
|
|
661
835
|
agent: OMAR_ORCHESTRATOR_AGENT,
|
|
662
|
-
payload: {
|
|
836
|
+
payload: {
|
|
837
|
+
runId,
|
|
838
|
+
mode,
|
|
839
|
+
personas,
|
|
840
|
+
basePersonas,
|
|
841
|
+
roster,
|
|
842
|
+
maxParallel,
|
|
843
|
+
maxCostUsd,
|
|
844
|
+
dryRun,
|
|
845
|
+
personaRouting,
|
|
846
|
+
},
|
|
663
847
|
runId,
|
|
664
848
|
}));
|
|
665
849
|
}
|
|
@@ -667,8 +851,8 @@ export async function runOmarGateOrchestrator({
|
|
|
667
851
|
const detSummary = deterministic?.summary || { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
668
852
|
const detFindings = deterministic?.findings || [];
|
|
669
853
|
|
|
670
|
-
// Per-persona cost budget = global / persona count (with minimum floor)
|
|
671
|
-
const perPersonaCost = Math.max(0.25, maxCostUsd / personas.length);
|
|
854
|
+
// Per-persona cost budget = global / effective persona count (with minimum floor).
|
|
855
|
+
const perPersonaCost = personas.length > 0 ? Math.max(0.25, maxCostUsd / personas.length) : 0;
|
|
672
856
|
let runningCostUsd = 0;
|
|
673
857
|
|
|
674
858
|
const personaResults = await runWithConcurrency(personas, maxParallel, async (personaId) => {
|
|
@@ -711,6 +895,11 @@ export async function runOmarGateOrchestrator({
|
|
|
711
895
|
}
|
|
712
896
|
|
|
713
897
|
const personaStart = Date.now();
|
|
898
|
+
const personaDeterministic = buildPersonaScopedDeterministic(
|
|
899
|
+
deterministic,
|
|
900
|
+
personaId,
|
|
901
|
+
personaRouting
|
|
902
|
+
);
|
|
714
903
|
|
|
715
904
|
if (onEvent) {
|
|
716
905
|
onEvent(createAgentEvent({
|
|
@@ -735,7 +924,7 @@ export async function runOmarGateOrchestrator({
|
|
|
735
924
|
targetPath,
|
|
736
925
|
mode,
|
|
737
926
|
runId,
|
|
738
|
-
deterministic,
|
|
927
|
+
deterministic: personaDeterministic,
|
|
739
928
|
outputDir,
|
|
740
929
|
provider,
|
|
741
930
|
model,
|
|
@@ -788,7 +977,7 @@ export async function runOmarGateOrchestrator({
|
|
|
788
977
|
const systemPrompt = buildPersonaReviewPrompt({
|
|
789
978
|
personaId,
|
|
790
979
|
targetPath,
|
|
791
|
-
deterministicSummary: detSummary,
|
|
980
|
+
deterministicSummary: personaDeterministic?.summary || detSummary,
|
|
792
981
|
});
|
|
793
982
|
|
|
794
983
|
const result = await runAiReviewLayer({
|
|
@@ -801,11 +990,11 @@ export async function runOmarGateOrchestrator({
|
|
|
801
990
|
personaId
|
|
802
991
|
),
|
|
803
992
|
deterministic: {
|
|
804
|
-
summary: detSummary,
|
|
805
|
-
findings: detFindings,
|
|
806
|
-
scope: deterministic?.scope || {},
|
|
807
|
-
layers: deterministic?.layers || {},
|
|
808
|
-
metadata: deterministic?.metadata || {},
|
|
993
|
+
summary: personaDeterministic?.summary || detSummary,
|
|
994
|
+
findings: personaDeterministic?.findings || detFindings,
|
|
995
|
+
scope: personaDeterministic?.scope || deterministic?.scope || {},
|
|
996
|
+
layers: personaDeterministic?.layers || deterministic?.layers || {},
|
|
997
|
+
metadata: personaDeterministic?.metadata || deterministic?.metadata || {},
|
|
809
998
|
},
|
|
810
999
|
outputDir,
|
|
811
1000
|
provider: provider || undefined,
|
|
@@ -982,7 +1171,9 @@ export async function runOmarGateOrchestrator({
|
|
|
982
1171
|
const result = {
|
|
983
1172
|
runId,
|
|
984
1173
|
mode,
|
|
1174
|
+
basePersonas,
|
|
985
1175
|
roster,
|
|
1176
|
+
personaRouting,
|
|
986
1177
|
personas: settled.map((r) => ({
|
|
987
1178
|
id: r.personaId,
|
|
988
1179
|
identity: r.persona,
|
|
@@ -1051,6 +1242,7 @@ export async function runOmarGateOrchestrator({
|
|
|
1051
1242
|
findings: reconciledFindings.length,
|
|
1052
1243
|
summary: result.summary,
|
|
1053
1244
|
reconciliation: result.reconciliation,
|
|
1245
|
+
personaRouting,
|
|
1054
1246
|
totalCostUsd: totalCost,
|
|
1055
1247
|
totalDurationMs: totalDuration,
|
|
1056
1248
|
},
|