sentinelayer-cli 0.8.11 → 0.8.12
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 +4 -4
- package/src/agents/jules/stream.js +2 -12
- package/src/audit/orchestrator.js +471 -114
- package/src/audit/persona-loop.js +1342 -0
- package/src/audit/registry.js +58 -2
- package/src/commands/audit.js +42 -1
- package/src/commands/legacy-args.js +28 -1
- package/src/commands/session.js +80 -20
- package/src/cost/history.js +41 -21
- package/src/events/schema.js +27 -1
- package/src/legacy-cli.js +76 -1
- package/src/review/omargate-cache.js +285 -0
- package/src/review/omargate-orchestrator.js +586 -3
- package/src/review/report.js +128 -2
- package/src/session/senti-naming.js +36 -0
- package/src/session/sync.js +23 -0
|
@@ -3,8 +3,10 @@ import fsp from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { resolveOutputRoot } from "../config/service.js";
|
|
6
|
+
import { createAgentEvent } from "../events/schema.js";
|
|
6
7
|
import { resolveCodebaseIngest } from "../ingest/engine.js";
|
|
7
8
|
import { runDeterministicReviewPipeline } from "../review/local-review.js";
|
|
9
|
+
import { loadOmarGateDeterministicCache } from "../review/omargate-cache.js";
|
|
8
10
|
import {
|
|
9
11
|
renderArchitectureSpecialistMarkdown,
|
|
10
12
|
runArchitectureSpecialist,
|
|
@@ -42,6 +44,15 @@ import {
|
|
|
42
44
|
buildSharedMemoryCorpus,
|
|
43
45
|
queryHybridRetriever,
|
|
44
46
|
} from "../memory/retrieval.js";
|
|
47
|
+
import { runPersonaAgenticLoop } from "./persona-loop.js";
|
|
48
|
+
|
|
49
|
+
const AUDIT_ORCHESTRATOR_AGENT = Object.freeze({
|
|
50
|
+
id: "audit-orchestrator",
|
|
51
|
+
persona: "Audit Orchestrator",
|
|
52
|
+
shortName: "Audit",
|
|
53
|
+
color: "cyan",
|
|
54
|
+
avatar: "A",
|
|
55
|
+
});
|
|
45
56
|
|
|
46
57
|
function normalizeString(value) {
|
|
47
58
|
return String(value || "").trim();
|
|
@@ -84,6 +95,34 @@ function severitySummary(findings = []) {
|
|
|
84
95
|
return summary;
|
|
85
96
|
}
|
|
86
97
|
|
|
98
|
+
function emitAuditEvent(onEvent, runId, event, payload = {}) {
|
|
99
|
+
if (!onEvent) return;
|
|
100
|
+
onEvent(createAgentEvent({
|
|
101
|
+
event,
|
|
102
|
+
agent: AUDIT_ORCHESTRATOR_AGENT,
|
|
103
|
+
payload,
|
|
104
|
+
runId,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function emitProgressShadow(onEvent, runId, phase, message, extra = {}) {
|
|
109
|
+
emitAuditEvent(onEvent, runId, "progress", {
|
|
110
|
+
phase,
|
|
111
|
+
message,
|
|
112
|
+
shadowFor: extra.shadowFor || phase,
|
|
113
|
+
...extra,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function emitAuditLifecycleEvent(onEvent, runId, event, payload = {}, shadowMessage = "") {
|
|
118
|
+
emitAuditEvent(onEvent, runId, event, payload);
|
|
119
|
+
if (shadowMessage) {
|
|
120
|
+
emitProgressShadow(onEvent, runId, payload.phase || event, shadowMessage, {
|
|
121
|
+
shadowFor: event,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
87
126
|
function routeFindingToAgentId(finding = {}) {
|
|
88
127
|
const file = normalizeString(finding.file).toLowerCase();
|
|
89
128
|
const message = normalizeString(finding.message).toLowerCase();
|
|
@@ -142,6 +181,11 @@ function computeConfidenceFloor(base, findingCount) {
|
|
|
142
181
|
return Math.max(0.5, Math.min(0.99, normalizedFloor - damping));
|
|
143
182
|
}
|
|
144
183
|
|
|
184
|
+
function normalizeIsolationMode(value) {
|
|
185
|
+
const normalized = normalizeString(value).toLowerCase();
|
|
186
|
+
return normalized === "relaxed" ? "relaxed" : "strict";
|
|
187
|
+
}
|
|
188
|
+
|
|
145
189
|
async function runWithConcurrency(items, maxParallel, worker) {
|
|
146
190
|
const results = [];
|
|
147
191
|
const queue = [...items];
|
|
@@ -182,6 +226,9 @@ Run ID: ${report.runId}
|
|
|
182
226
|
Target: ${report.targetPath}
|
|
183
227
|
Max parallel: ${report.maxParallel}
|
|
184
228
|
Dry run: ${report.dryRun ? "yes" : "no"}
|
|
229
|
+
Persona isolation: ${report.isolation || "strict"}
|
|
230
|
+
Seed from deterministic: ${report.seedFromDeterministic === false ? "no" : "yes"}
|
|
231
|
+
OmarGate reuse: ${report.omargateReuse?.used ? `yes (${report.omargateReuse.runId})` : report.omargateReuse?.requested ? `requested ${report.omargateReuse.requested} (${report.omargateReuse.reason || "unavailable"})` : "no"}
|
|
185
232
|
|
|
186
233
|
Summary:
|
|
187
234
|
- Findings: P0=${report.summary.P0} P1=${report.summary.P1} P2=${report.summary.P2} P3=${report.summary.P3}
|
|
@@ -209,6 +256,7 @@ Ingest:
|
|
|
209
256
|
Deterministic baseline:
|
|
210
257
|
- Run ID: ${report.deterministicBaseline.runId || "n/a"}
|
|
211
258
|
- Report: ${report.deterministicBaseline.reportPath || "n/a"}
|
|
259
|
+
- Reused OmarGate run: ${report.omargateReuse?.used ? report.omargateReuse.runId : "n/a"}
|
|
212
260
|
- Summary: P0=${report.deterministicBaseline.summary.P0} P1=${report.deterministicBaseline.summary.P1} P2=${report.deterministicBaseline.summary.P2} P3=${report.deterministicBaseline.summary.P3}
|
|
213
261
|
|
|
214
262
|
Agent outcomes:
|
|
@@ -216,6 +264,110 @@ ${agentLines || "- none"}
|
|
|
216
264
|
`;
|
|
217
265
|
}
|
|
218
266
|
|
|
267
|
+
async function buildSpecialistSeed({
|
|
268
|
+
agent,
|
|
269
|
+
deterministicBaseline,
|
|
270
|
+
ingest,
|
|
271
|
+
agentsDirectory,
|
|
272
|
+
}) {
|
|
273
|
+
let findings = [];
|
|
274
|
+
let summary = severitySummary(findings);
|
|
275
|
+
let confidence = computeConfidenceFloor(agent.confidenceFloor, findings.length);
|
|
276
|
+
let specialistReportPath = "";
|
|
277
|
+
|
|
278
|
+
if (agent.id === "security") {
|
|
279
|
+
const securitySpecialist = runSecuritySpecialist({
|
|
280
|
+
findings: deterministicBaseline.findings,
|
|
281
|
+
});
|
|
282
|
+
findings = securitySpecialist.findings;
|
|
283
|
+
summary = securitySpecialist.summary;
|
|
284
|
+
confidence = securitySpecialist.confidence;
|
|
285
|
+
specialistReportPath = path.join(agentsDirectory, "SECURITY_AGENT_REPORT.md");
|
|
286
|
+
await fsp.writeFile(
|
|
287
|
+
specialistReportPath,
|
|
288
|
+
`${renderSecuritySpecialistMarkdown(securitySpecialist).trim()}\n`,
|
|
289
|
+
"utf-8"
|
|
290
|
+
);
|
|
291
|
+
} else if (agent.id === "architecture") {
|
|
292
|
+
const architectureSpecialist = runArchitectureSpecialist({
|
|
293
|
+
findings: deterministicBaseline.findings,
|
|
294
|
+
ingest,
|
|
295
|
+
});
|
|
296
|
+
findings = architectureSpecialist.findings;
|
|
297
|
+
summary = architectureSpecialist.summary;
|
|
298
|
+
confidence = architectureSpecialist.confidence;
|
|
299
|
+
specialistReportPath = path.join(agentsDirectory, "ARCHITECTURE_AGENT_REPORT.md");
|
|
300
|
+
await fsp.writeFile(
|
|
301
|
+
specialistReportPath,
|
|
302
|
+
`${renderArchitectureSpecialistMarkdown(architectureSpecialist).trim()}\n`,
|
|
303
|
+
"utf-8"
|
|
304
|
+
);
|
|
305
|
+
} else if (agent.id === "testing") {
|
|
306
|
+
const testingSpecialist = runTestingSpecialist({
|
|
307
|
+
findings: deterministicBaseline.findings,
|
|
308
|
+
ingest,
|
|
309
|
+
});
|
|
310
|
+
findings = testingSpecialist.findings;
|
|
311
|
+
summary = testingSpecialist.summary;
|
|
312
|
+
confidence = testingSpecialist.confidence;
|
|
313
|
+
specialistReportPath = path.join(agentsDirectory, "TESTING_AGENT_REPORT.md");
|
|
314
|
+
await fsp.writeFile(
|
|
315
|
+
specialistReportPath,
|
|
316
|
+
`${renderTestingSpecialistMarkdown(testingSpecialist).trim()}\n`,
|
|
317
|
+
"utf-8"
|
|
318
|
+
);
|
|
319
|
+
} else if (agent.id === "performance") {
|
|
320
|
+
const performanceSpecialist = runPerformanceSpecialist({
|
|
321
|
+
findings: deterministicBaseline.findings,
|
|
322
|
+
ingest,
|
|
323
|
+
});
|
|
324
|
+
findings = performanceSpecialist.findings;
|
|
325
|
+
summary = performanceSpecialist.summary;
|
|
326
|
+
confidence = performanceSpecialist.confidence;
|
|
327
|
+
specialistReportPath = path.join(agentsDirectory, "PERFORMANCE_AGENT_REPORT.md");
|
|
328
|
+
await fsp.writeFile(
|
|
329
|
+
specialistReportPath,
|
|
330
|
+
`${renderPerformanceSpecialistMarkdown(performanceSpecialist).trim()}\n`,
|
|
331
|
+
"utf-8"
|
|
332
|
+
);
|
|
333
|
+
} else if (agent.id === "compliance") {
|
|
334
|
+
const complianceSpecialist = runComplianceSpecialist({
|
|
335
|
+
findings: deterministicBaseline.findings,
|
|
336
|
+
ingest,
|
|
337
|
+
});
|
|
338
|
+
findings = complianceSpecialist.findings;
|
|
339
|
+
summary = complianceSpecialist.summary;
|
|
340
|
+
confidence = complianceSpecialist.confidence;
|
|
341
|
+
specialistReportPath = path.join(agentsDirectory, "COMPLIANCE_AGENT_REPORT.md");
|
|
342
|
+
await fsp.writeFile(
|
|
343
|
+
specialistReportPath,
|
|
344
|
+
`${renderComplianceSpecialistMarkdown(complianceSpecialist).trim()}\n`,
|
|
345
|
+
"utf-8"
|
|
346
|
+
);
|
|
347
|
+
} else if (agent.id === "documentation") {
|
|
348
|
+
const documentationSpecialist = runDocumentationSpecialist({
|
|
349
|
+
findings: deterministicBaseline.findings,
|
|
350
|
+
ingest,
|
|
351
|
+
});
|
|
352
|
+
findings = documentationSpecialist.findings;
|
|
353
|
+
summary = documentationSpecialist.summary;
|
|
354
|
+
confidence = documentationSpecialist.confidence;
|
|
355
|
+
specialistReportPath = path.join(agentsDirectory, "DOCUMENTATION_AGENT_REPORT.md");
|
|
356
|
+
await fsp.writeFile(
|
|
357
|
+
specialistReportPath,
|
|
358
|
+
`${renderDocumentationSpecialistMarkdown(documentationSpecialist).trim()}\n`,
|
|
359
|
+
"utf-8"
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
findings,
|
|
365
|
+
summary,
|
|
366
|
+
confidence,
|
|
367
|
+
specialistReportPath,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
219
371
|
export async function runAuditOrchestrator({
|
|
220
372
|
targetPath,
|
|
221
373
|
agents = [],
|
|
@@ -223,14 +375,24 @@ export async function runAuditOrchestrator({
|
|
|
223
375
|
outputDir = "",
|
|
224
376
|
dryRun = false,
|
|
225
377
|
refreshIngest = false,
|
|
378
|
+
provider = null,
|
|
379
|
+
onEvent = null,
|
|
380
|
+
clientFactory = null,
|
|
381
|
+
isolation = "strict",
|
|
382
|
+
seedFromDeterministic = true,
|
|
383
|
+
reuseOmarGate = "",
|
|
226
384
|
} = {}) {
|
|
227
385
|
const normalizedTargetPath = path.resolve(String(targetPath || "."));
|
|
386
|
+
const isolationMode = normalizeIsolationMode(isolation);
|
|
387
|
+
const useDeterministicSeed = seedFromDeterministic !== false;
|
|
388
|
+
const requestedOmarGateReuse = normalizeString(reuseOmarGate);
|
|
228
389
|
const outputRoot = await resolveOutputRoot({
|
|
229
390
|
cwd: normalizedTargetPath,
|
|
230
391
|
outputDirOverride: outputDir,
|
|
231
392
|
env: process.env,
|
|
232
393
|
});
|
|
233
394
|
const runId = `audit-${formatTimestampToken()}-${randomUUID().slice(0, 8)}`;
|
|
395
|
+
const startedAt = Date.now();
|
|
234
396
|
const runDirectory = path.join(outputRoot, "audits", runId);
|
|
235
397
|
const agentsDirectory = path.join(runDirectory, "agents");
|
|
236
398
|
await fsp.mkdir(agentsDirectory, { recursive: true });
|
|
@@ -238,13 +400,55 @@ export async function runAuditOrchestrator({
|
|
|
238
400
|
runId,
|
|
239
401
|
scope: "audit-orchestrator",
|
|
240
402
|
});
|
|
403
|
+
const selectedAgentIds = agents.map((agent) => agent.id);
|
|
404
|
+
const normalizedMaxParallel = Math.max(1, Math.floor(Number(maxParallel || 1)));
|
|
405
|
+
|
|
406
|
+
emitAuditLifecycleEvent(
|
|
407
|
+
onEvent,
|
|
408
|
+
runId,
|
|
409
|
+
"orchestrator_start",
|
|
410
|
+
{
|
|
411
|
+
runId,
|
|
412
|
+
targetPath: normalizedTargetPath,
|
|
413
|
+
selectedAgents: selectedAgentIds,
|
|
414
|
+
agentCount: selectedAgentIds.length,
|
|
415
|
+
maxParallel: normalizedMaxParallel,
|
|
416
|
+
dryRun: Boolean(dryRun),
|
|
417
|
+
isolation: isolationMode,
|
|
418
|
+
seedFromDeterministic: useDeterministicSeed,
|
|
419
|
+
reuseOmarGate: requestedOmarGateReuse || "",
|
|
420
|
+
},
|
|
421
|
+
`Audit orchestrator starting with ${selectedAgentIds.length} agent(s).`
|
|
422
|
+
);
|
|
241
423
|
|
|
424
|
+
const ingestStartedAt = Date.now();
|
|
425
|
+
emitAuditLifecycleEvent(
|
|
426
|
+
onEvent,
|
|
427
|
+
runId,
|
|
428
|
+
"phase_start",
|
|
429
|
+
{ phase: "ingest", targetPath: normalizedTargetPath, refresh: Boolean(refreshIngest) },
|
|
430
|
+
"Starting codebase ingest."
|
|
431
|
+
);
|
|
242
432
|
const ingestResolution = await resolveCodebaseIngest({
|
|
243
433
|
rootPath: normalizedTargetPath,
|
|
244
434
|
outputDir,
|
|
245
435
|
refresh: Boolean(refreshIngest),
|
|
246
436
|
});
|
|
247
437
|
const ingest = ingestResolution.ingest;
|
|
438
|
+
emitAuditLifecycleEvent(
|
|
439
|
+
onEvent,
|
|
440
|
+
runId,
|
|
441
|
+
"phase_complete",
|
|
442
|
+
{
|
|
443
|
+
phase: "ingest",
|
|
444
|
+
durationMs: Math.max(0, Date.now() - ingestStartedAt),
|
|
445
|
+
filesScanned: Number(ingest?.summary?.filesScanned || 0),
|
|
446
|
+
totalLoc: Number(ingest?.summary?.totalLoc || 0),
|
|
447
|
+
refreshed: Boolean(ingestResolution.refreshed),
|
|
448
|
+
stale: Boolean(ingestResolution.stale),
|
|
449
|
+
},
|
|
450
|
+
`Ingest complete: ${Number(ingest?.summary?.filesScanned || 0)} files.`
|
|
451
|
+
);
|
|
248
452
|
|
|
249
453
|
let deterministicBaseline = {
|
|
250
454
|
runId: "",
|
|
@@ -253,25 +457,113 @@ export async function runAuditOrchestrator({
|
|
|
253
457
|
summary: { P0: 0, P1: 0, P2: 0, P3: 0, blocking: false },
|
|
254
458
|
findings: [],
|
|
255
459
|
};
|
|
460
|
+
let omargateReuse = {
|
|
461
|
+
requested: requestedOmarGateReuse,
|
|
462
|
+
used: false,
|
|
463
|
+
runId: "",
|
|
464
|
+
deterministicRunId: "",
|
|
465
|
+
artifactPath: "",
|
|
466
|
+
reason: requestedOmarGateReuse ? (dryRun ? "dry_run" : "not_found") : "not_requested",
|
|
467
|
+
};
|
|
468
|
+
const baselineStartedAt = Date.now();
|
|
469
|
+
emitAuditLifecycleEvent(
|
|
470
|
+
onEvent,
|
|
471
|
+
runId,
|
|
472
|
+
"phase_start",
|
|
473
|
+
{
|
|
474
|
+
phase: "deterministic_baseline",
|
|
475
|
+
skipped: Boolean(dryRun),
|
|
476
|
+
reuseRequested: Boolean(requestedOmarGateReuse),
|
|
477
|
+
requestedOmarGateRunId: requestedOmarGateReuse,
|
|
478
|
+
},
|
|
479
|
+
dryRun
|
|
480
|
+
? "Skipping deterministic baseline for dry run."
|
|
481
|
+
: requestedOmarGateReuse
|
|
482
|
+
? "Resolving OmarGate deterministic baseline reuse."
|
|
483
|
+
: "Starting deterministic baseline."
|
|
484
|
+
);
|
|
256
485
|
if (!dryRun) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
486
|
+
let reusedBaseline = null;
|
|
487
|
+
if (requestedOmarGateReuse) {
|
|
488
|
+
const reused = await loadOmarGateDeterministicCache({
|
|
489
|
+
targetPath: normalizedTargetPath,
|
|
490
|
+
outputDir,
|
|
491
|
+
runIdOrLatest: requestedOmarGateReuse,
|
|
492
|
+
});
|
|
493
|
+
if (reused.found) {
|
|
494
|
+
const cache = reused.cache || {};
|
|
495
|
+
reusedBaseline = {
|
|
496
|
+
runId: cache.deterministicRunId || cache.runId || reused.runId,
|
|
497
|
+
reportPath: cache.source?.reportPath || "",
|
|
498
|
+
reportJsonPath: cache.artifacts?.jsonPath || "",
|
|
499
|
+
summary: cache.summary || severitySummary(cache.findings || []),
|
|
500
|
+
findings: Array.isArray(cache.findings) ? cache.findings : [],
|
|
501
|
+
reusedFromOmarGate: true,
|
|
502
|
+
reusedOmarGateRunId: reused.runId,
|
|
503
|
+
reuseArtifactPath: reused.artifactPath,
|
|
504
|
+
};
|
|
505
|
+
omargateReuse = {
|
|
506
|
+
requested: requestedOmarGateReuse,
|
|
507
|
+
used: true,
|
|
508
|
+
runId: reused.runId,
|
|
509
|
+
deterministicRunId: cache.deterministicRunId || "",
|
|
510
|
+
artifactPath: reused.artifactPath,
|
|
511
|
+
reason: "",
|
|
512
|
+
};
|
|
513
|
+
} else {
|
|
514
|
+
omargateReuse = {
|
|
515
|
+
requested: requestedOmarGateReuse,
|
|
516
|
+
used: false,
|
|
517
|
+
runId: "",
|
|
518
|
+
deterministicRunId: "",
|
|
519
|
+
artifactPath: "",
|
|
520
|
+
reason: reused.reason || "not_found",
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (reusedBaseline) {
|
|
525
|
+
deterministicBaseline = reusedBaseline;
|
|
526
|
+
} else {
|
|
527
|
+
const deterministic = await runDeterministicReviewPipeline({
|
|
528
|
+
targetPath: normalizedTargetPath,
|
|
529
|
+
mode: "full",
|
|
530
|
+
outputDir,
|
|
531
|
+
});
|
|
532
|
+
deterministicBaseline = {
|
|
533
|
+
runId: deterministic.runId,
|
|
534
|
+
reportPath: deterministic.artifacts.markdownPath,
|
|
535
|
+
reportJsonPath: deterministic.artifacts.jsonPath,
|
|
536
|
+
summary: deterministic.summary,
|
|
537
|
+
findings: deterministic.findings,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
emitAuditLifecycleEvent(
|
|
542
|
+
onEvent,
|
|
543
|
+
runId,
|
|
544
|
+
"phase_complete",
|
|
545
|
+
{
|
|
546
|
+
phase: "deterministic_baseline",
|
|
547
|
+
skipped: Boolean(dryRun),
|
|
548
|
+
reused: Boolean(omargateReuse.used),
|
|
549
|
+
omargateReuse,
|
|
550
|
+
durationMs: Math.max(0, Date.now() - baselineStartedAt),
|
|
551
|
+
findingCount: deterministicBaseline.findings.length,
|
|
552
|
+
summary: deterministicBaseline.summary,
|
|
553
|
+
},
|
|
554
|
+
dryRun
|
|
555
|
+
? "Deterministic baseline skipped."
|
|
556
|
+
: omargateReuse.used
|
|
557
|
+
? `Reused OmarGate deterministic baseline ${omargateReuse.runId}: ${deterministicBaseline.findings.length} finding(s).`
|
|
558
|
+
: `Deterministic baseline complete: ${deterministicBaseline.findings.length} finding(s).`
|
|
559
|
+
);
|
|
560
|
+
if (useDeterministicSeed) {
|
|
561
|
+
appendBlackboardFindings(blackboard, {
|
|
562
|
+
agentId: "omar",
|
|
563
|
+
findings: deterministicBaseline.findings,
|
|
564
|
+
source: omargateReuse.used ? "omargate-reuse" : "deterministic-baseline",
|
|
261
565
|
});
|
|
262
|
-
deterministicBaseline = {
|
|
263
|
-
runId: deterministic.runId,
|
|
264
|
-
reportPath: deterministic.artifacts.markdownPath,
|
|
265
|
-
reportJsonPath: deterministic.artifacts.jsonPath,
|
|
266
|
-
summary: deterministic.summary,
|
|
267
|
-
findings: deterministic.findings,
|
|
268
|
-
};
|
|
269
566
|
}
|
|
270
|
-
appendBlackboardFindings(blackboard, {
|
|
271
|
-
agentId: "omar",
|
|
272
|
-
findings: deterministicBaseline.findings,
|
|
273
|
-
source: "deterministic-baseline",
|
|
274
|
-
});
|
|
275
567
|
const memoryProvider = resolveMemoryProvider(process.env);
|
|
276
568
|
const memoryApiEndpoint = normalizeString(process.env.SENTINELAYER_MEMORY_API_ENDPOINT);
|
|
277
569
|
const memoryApiKey = normalizeString(
|
|
@@ -288,16 +580,39 @@ export async function runAuditOrchestrator({
|
|
|
288
580
|
const sharedMemoryQueries = [];
|
|
289
581
|
|
|
290
582
|
const routeBuckets = new Map();
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
583
|
+
if (useDeterministicSeed) {
|
|
584
|
+
for (const finding of deterministicBaseline.findings) {
|
|
585
|
+
const bucketId = routeFindingToAgentId(finding);
|
|
586
|
+
const existing = routeBuckets.get(bucketId) || [];
|
|
587
|
+
existing.push(finding);
|
|
588
|
+
routeBuckets.set(bucketId, existing);
|
|
589
|
+
}
|
|
296
590
|
}
|
|
297
591
|
|
|
298
|
-
const
|
|
299
|
-
|
|
592
|
+
const dispatchStartedAt = Date.now();
|
|
593
|
+
emitAuditLifecycleEvent(
|
|
594
|
+
onEvent,
|
|
595
|
+
runId,
|
|
596
|
+
"phase_start",
|
|
597
|
+
{ phase: "dispatch", agentCount: agents.length, maxParallel: normalizedMaxParallel },
|
|
598
|
+
`Dispatching ${agents.length} audit persona(s).`
|
|
599
|
+
);
|
|
600
|
+
const agentResults = await runWithConcurrency(agents, normalizedMaxParallel, async (agent) => {
|
|
300
601
|
const agentStart = Date.now();
|
|
602
|
+
emitAuditLifecycleEvent(
|
|
603
|
+
onEvent,
|
|
604
|
+
runId,
|
|
605
|
+
"dispatch",
|
|
606
|
+
{
|
|
607
|
+
phase: "dispatch",
|
|
608
|
+
agentId: agent.id,
|
|
609
|
+
persona: agent.persona,
|
|
610
|
+
domain: agent.domain,
|
|
611
|
+
isolation: isolationMode,
|
|
612
|
+
seedFromDeterministic: useDeterministicSeed,
|
|
613
|
+
},
|
|
614
|
+
`Dispatching ${agent.id} persona.`
|
|
615
|
+
);
|
|
301
616
|
const sharedContext = queryBlackboard(blackboard, {
|
|
302
617
|
query: `${agent.id} ${agent.domain} ${agent.persona}`,
|
|
303
618
|
agentId: agent.id,
|
|
@@ -321,96 +636,60 @@ export async function runAuditOrchestrator({
|
|
|
321
636
|
resultCount: Array.isArray(hybridContext.results) ? hybridContext.results.length : 0,
|
|
322
637
|
apiError: hybridContext.apiError || "",
|
|
323
638
|
});
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
);
|
|
356
|
-
} else if (agent.id === "testing") {
|
|
357
|
-
const testingSpecialist = runTestingSpecialist({
|
|
358
|
-
findings: deterministicBaseline.findings,
|
|
359
|
-
ingest,
|
|
360
|
-
});
|
|
361
|
-
findings = testingSpecialist.findings;
|
|
362
|
-
summary = testingSpecialist.summary;
|
|
363
|
-
confidence = testingSpecialist.confidence;
|
|
364
|
-
specialistReportPath = path.join(agentsDirectory, "TESTING_AGENT_REPORT.md");
|
|
365
|
-
await fsp.writeFile(
|
|
366
|
-
specialistReportPath,
|
|
367
|
-
`${renderTestingSpecialistMarkdown(testingSpecialist).trim()}\n`,
|
|
368
|
-
"utf-8"
|
|
369
|
-
);
|
|
370
|
-
} else if (agent.id === "performance") {
|
|
371
|
-
const performanceSpecialist = runPerformanceSpecialist({
|
|
372
|
-
findings: deterministicBaseline.findings,
|
|
373
|
-
ingest,
|
|
374
|
-
});
|
|
375
|
-
findings = performanceSpecialist.findings;
|
|
376
|
-
summary = performanceSpecialist.summary;
|
|
377
|
-
confidence = performanceSpecialist.confidence;
|
|
378
|
-
specialistReportPath = path.join(agentsDirectory, "PERFORMANCE_AGENT_REPORT.md");
|
|
379
|
-
await fsp.writeFile(
|
|
380
|
-
specialistReportPath,
|
|
381
|
-
`${renderPerformanceSpecialistMarkdown(performanceSpecialist).trim()}\n`,
|
|
382
|
-
"utf-8"
|
|
383
|
-
);
|
|
384
|
-
} else if (agent.id === "compliance") {
|
|
385
|
-
const complianceSpecialist = runComplianceSpecialist({
|
|
386
|
-
findings: deterministicBaseline.findings,
|
|
387
|
-
ingest,
|
|
388
|
-
});
|
|
389
|
-
findings = complianceSpecialist.findings;
|
|
390
|
-
summary = complianceSpecialist.summary;
|
|
391
|
-
confidence = complianceSpecialist.confidence;
|
|
392
|
-
specialistReportPath = path.join(agentsDirectory, "COMPLIANCE_AGENT_REPORT.md");
|
|
393
|
-
await fsp.writeFile(
|
|
394
|
-
specialistReportPath,
|
|
395
|
-
`${renderComplianceSpecialistMarkdown(complianceSpecialist).trim()}\n`,
|
|
396
|
-
"utf-8"
|
|
397
|
-
);
|
|
398
|
-
} else if (agent.id === "documentation") {
|
|
399
|
-
const documentationSpecialist = runDocumentationSpecialist({
|
|
400
|
-
findings: deterministicBaseline.findings,
|
|
639
|
+
const personaDeterministicBaseline = useDeterministicSeed
|
|
640
|
+
? deterministicBaseline
|
|
641
|
+
: { ...deterministicBaseline, findings: [] };
|
|
642
|
+
const routedFindings = useDeterministicSeed ? routeBuckets.get(agent.id) || [] : [];
|
|
643
|
+
const specialistSeed = useDeterministicSeed
|
|
644
|
+
? await buildSpecialistSeed({
|
|
645
|
+
agent,
|
|
646
|
+
deterministicBaseline,
|
|
647
|
+
ingest,
|
|
648
|
+
agentsDirectory,
|
|
649
|
+
})
|
|
650
|
+
: {
|
|
651
|
+
findings: [],
|
|
652
|
+
summary: severitySummary([]),
|
|
653
|
+
confidence: computeConfidenceFloor(agent.confidenceFloor, 0),
|
|
654
|
+
specialistReportPath: "",
|
|
655
|
+
};
|
|
656
|
+
let findings = specialistSeed.findings.length > 0 ? specialistSeed.findings : routedFindings;
|
|
657
|
+
let summary = specialistSeed.findings.length > 0
|
|
658
|
+
? specialistSeed.summary
|
|
659
|
+
: severitySummary(findings);
|
|
660
|
+
let confidence = specialistSeed.findings.length > 0
|
|
661
|
+
? specialistSeed.confidence
|
|
662
|
+
: computeConfidenceFloor(agent.confidenceFloor, findings.length);
|
|
663
|
+
let specialistReportPath = specialistSeed.specialistReportPath;
|
|
664
|
+
let agenticReport = null;
|
|
665
|
+
|
|
666
|
+
if (agent.id !== "frontend") {
|
|
667
|
+
agenticReport = await runPersonaAgenticLoop({
|
|
668
|
+
agent,
|
|
669
|
+
rootPath: normalizedTargetPath,
|
|
401
670
|
ingest,
|
|
671
|
+
deterministicBaseline: personaDeterministicBaseline,
|
|
672
|
+
seedFindings: findings,
|
|
673
|
+
sharedContext,
|
|
674
|
+
hybridContext,
|
|
675
|
+
artifactDir: agentsDirectory,
|
|
676
|
+
provider,
|
|
677
|
+
onEvent,
|
|
678
|
+
clientFactory,
|
|
679
|
+
dryRun: Boolean(dryRun),
|
|
680
|
+
isolation: isolationMode,
|
|
402
681
|
});
|
|
403
|
-
findings =
|
|
404
|
-
summary =
|
|
405
|
-
confidence =
|
|
406
|
-
specialistReportPath = path.join(agentsDirectory, "DOCUMENTATION_AGENT_REPORT.md");
|
|
407
|
-
await fsp.writeFile(
|
|
408
|
-
specialistReportPath,
|
|
409
|
-
`${renderDocumentationSpecialistMarkdown(documentationSpecialist).trim()}\n`,
|
|
410
|
-
"utf-8"
|
|
411
|
-
);
|
|
682
|
+
findings = agenticReport.findings;
|
|
683
|
+
summary = agenticReport.summary;
|
|
684
|
+
confidence = agenticReport.confidence;
|
|
412
685
|
}
|
|
413
686
|
|
|
687
|
+
const agentStatus = summary.blocking
|
|
688
|
+
? "escalate"
|
|
689
|
+
: agenticReport && !["completed", "dry_run"].includes(agenticReport.status)
|
|
690
|
+
? agenticReport.status
|
|
691
|
+
: "ok";
|
|
692
|
+
|
|
414
693
|
const result = {
|
|
415
694
|
agentId: agent.id,
|
|
416
695
|
persona: agent.persona,
|
|
@@ -418,17 +697,34 @@ export async function runAuditOrchestrator({
|
|
|
418
697
|
permissionMode: agent.permissionMode,
|
|
419
698
|
maxTurns: agent.maxTurns,
|
|
420
699
|
confidenceFloor: agent.confidenceFloor,
|
|
700
|
+
isolation: isolationMode,
|
|
701
|
+
seedFromDeterministic: useDeterministicSeed,
|
|
702
|
+
routedSeedFindingCount: routedFindings.length,
|
|
703
|
+
specialistSeedFindingCount: specialistSeed.findings.length,
|
|
421
704
|
confidence,
|
|
422
705
|
findingCount: findings.length,
|
|
423
706
|
summary,
|
|
424
707
|
findings: findings.slice(0, 120),
|
|
425
|
-
status:
|
|
708
|
+
status: agentStatus,
|
|
426
709
|
startedAt: new Date(agentStart).toISOString(),
|
|
427
710
|
completedAt: new Date().toISOString(),
|
|
428
711
|
durationMs: Math.max(0, Date.now() - agentStart),
|
|
429
712
|
escalationTargets: agent.escalationTargets || [],
|
|
430
713
|
evidenceRequirements: agent.evidenceRequirements || [],
|
|
431
714
|
specialistReportPath,
|
|
715
|
+
agenticRunId: agenticReport?.runId || "",
|
|
716
|
+
agenticStatus: agenticReport?.status || (agent.id === "frontend" ? "preserved-frontend-flow" : ""),
|
|
717
|
+
agenticMessageHistoryLength: Number(agenticReport?.messageHistoryLength || 0),
|
|
718
|
+
swarm: agenticReport?.swarm || null,
|
|
719
|
+
usage: agenticReport?.usage || {
|
|
720
|
+
costUsd: 0,
|
|
721
|
+
outputTokens: 0,
|
|
722
|
+
toolCalls: 0,
|
|
723
|
+
durationMs: Math.max(0, Date.now() - agentStart),
|
|
724
|
+
filesRead: [],
|
|
725
|
+
},
|
|
726
|
+
grantedTools: agenticReport?.grantedTools || agent.tools || [],
|
|
727
|
+
availableTools: agenticReport?.availableTools || [],
|
|
432
728
|
sharedContextEntryCount: sharedContext.entries.length,
|
|
433
729
|
sharedContextPreview: sharedContext.entries.slice(0, 5).map((entry) => ({
|
|
434
730
|
entryId: entry.entryId,
|
|
@@ -451,8 +747,8 @@ export async function runAuditOrchestrator({
|
|
|
451
747
|
appendBlackboardFindings(blackboard, {
|
|
452
748
|
agentId: agent.id,
|
|
453
749
|
findings,
|
|
454
|
-
source: "specialist-agent",
|
|
455
|
-
note: `${agent.id}
|
|
750
|
+
source: agenticReport ? "persona-agentic-loop" : "specialist-agent",
|
|
751
|
+
note: `${agent.id} persona finding`,
|
|
456
752
|
confidence,
|
|
457
753
|
});
|
|
458
754
|
const agentPath = path.join(agentsDirectory, `${agent.id}.json`);
|
|
@@ -463,7 +759,35 @@ export async function runAuditOrchestrator({
|
|
|
463
759
|
};
|
|
464
760
|
});
|
|
465
761
|
agentResults.sort((left, right) => left.agentId.localeCompare(right.agentId));
|
|
762
|
+
emitAuditLifecycleEvent(
|
|
763
|
+
onEvent,
|
|
764
|
+
runId,
|
|
765
|
+
"phase_complete",
|
|
766
|
+
{
|
|
767
|
+
phase: "dispatch",
|
|
768
|
+
durationMs: Math.max(0, Date.now() - dispatchStartedAt),
|
|
769
|
+
agentCount: agentResults.length,
|
|
770
|
+
statuses: agentResults.reduce((acc, result) => {
|
|
771
|
+
const status = normalizeString(result.status) || "unknown";
|
|
772
|
+
acc[status] = (acc[status] || 0) + 1;
|
|
773
|
+
return acc;
|
|
774
|
+
}, {}),
|
|
775
|
+
},
|
|
776
|
+
`Dispatch complete: ${agentResults.length} persona result(s).`
|
|
777
|
+
);
|
|
466
778
|
|
|
779
|
+
const reconcileStartedAt = Date.now();
|
|
780
|
+
const candidateFindingCount = agentResults.reduce(
|
|
781
|
+
(count, agent) => count + (Array.isArray(agent.findings) ? agent.findings.length : 0),
|
|
782
|
+
0
|
|
783
|
+
);
|
|
784
|
+
emitAuditLifecycleEvent(
|
|
785
|
+
onEvent,
|
|
786
|
+
runId,
|
|
787
|
+
"reconcile_start",
|
|
788
|
+
{ phase: "reconcile", agentCount: agentResults.length, candidateFindingCount },
|
|
789
|
+
`Reconciling ${candidateFindingCount} candidate finding(s).`
|
|
790
|
+
);
|
|
467
791
|
const uniqueFindings = [];
|
|
468
792
|
const seen = new Set();
|
|
469
793
|
for (const agent of agentResults) {
|
|
@@ -479,6 +803,20 @@ export async function runAuditOrchestrator({
|
|
|
479
803
|
}
|
|
480
804
|
}
|
|
481
805
|
const summary = severitySummary(uniqueFindings);
|
|
806
|
+
emitAuditLifecycleEvent(
|
|
807
|
+
onEvent,
|
|
808
|
+
runId,
|
|
809
|
+
"reconcile_complete",
|
|
810
|
+
{
|
|
811
|
+
phase: "reconcile",
|
|
812
|
+
durationMs: Math.max(0, Date.now() - reconcileStartedAt),
|
|
813
|
+
candidateFindingCount,
|
|
814
|
+
findingCount: uniqueFindings.length,
|
|
815
|
+
dedupedCount: Math.max(0, candidateFindingCount - uniqueFindings.length),
|
|
816
|
+
summary,
|
|
817
|
+
},
|
|
818
|
+
`Reconcile complete: ${uniqueFindings.length} unique finding(s).`
|
|
819
|
+
);
|
|
482
820
|
const sharedMemoryArtifact = await writeBlackboardArtifact(blackboard, {
|
|
483
821
|
outputRoot,
|
|
484
822
|
});
|
|
@@ -494,7 +832,9 @@ export async function runAuditOrchestrator({
|
|
|
494
832
|
outputRoot,
|
|
495
833
|
runDirectory,
|
|
496
834
|
dryRun: Boolean(dryRun),
|
|
497
|
-
maxParallel:
|
|
835
|
+
maxParallel: normalizedMaxParallel,
|
|
836
|
+
isolation: isolationMode,
|
|
837
|
+
seedFromDeterministic: useDeterministicSeed,
|
|
498
838
|
durationMs: Math.max(0, Date.now() - startedAt),
|
|
499
839
|
ingest: {
|
|
500
840
|
summary: ingest.summary,
|
|
@@ -513,6 +853,7 @@ export async function runAuditOrchestrator({
|
|
|
513
853
|
},
|
|
514
854
|
},
|
|
515
855
|
deterministicBaseline,
|
|
856
|
+
omargateReuse,
|
|
516
857
|
sharedMemory: {
|
|
517
858
|
enabled: true,
|
|
518
859
|
artifactPath: sharedMemoryArtifact.artifactPath,
|
|
@@ -533,7 +874,7 @@ export async function runAuditOrchestrator({
|
|
|
533
874
|
historyRunDocumentCount: sharedMemoryCorpus.historyRunDocumentCount,
|
|
534
875
|
},
|
|
535
876
|
},
|
|
536
|
-
selectedAgents:
|
|
877
|
+
selectedAgents: selectedAgentIds,
|
|
537
878
|
agentResults,
|
|
538
879
|
summary,
|
|
539
880
|
};
|
|
@@ -546,6 +887,22 @@ export async function runAuditOrchestrator({
|
|
|
546
887
|
report,
|
|
547
888
|
runDirectory,
|
|
548
889
|
});
|
|
890
|
+
emitAuditLifecycleEvent(
|
|
891
|
+
onEvent,
|
|
892
|
+
runId,
|
|
893
|
+
"orchestrator_complete",
|
|
894
|
+
{
|
|
895
|
+
runId,
|
|
896
|
+
targetPath: normalizedTargetPath,
|
|
897
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
898
|
+
agentCount: agentResults.length,
|
|
899
|
+
summary,
|
|
900
|
+
reportJsonPath,
|
|
901
|
+
reportMarkdownPath,
|
|
902
|
+
ddPackageManifestPath: ddPackage.manifestPath || "",
|
|
903
|
+
},
|
|
904
|
+
`Audit orchestrator complete: P0=${summary.P0} P1=${summary.P1} P2=${summary.P2} P3=${summary.P3}.`
|
|
905
|
+
);
|
|
549
906
|
|
|
550
907
|
return {
|
|
551
908
|
...report,
|