sentinelayer-cli 0.4.5 → 0.6.2
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/README.md +996 -998
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -63
- package/src/agents/jules/config/definition.js +160 -209
- package/src/agents/jules/config/system-prompt.js +182 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -377
- package/src/agents/jules/loop.js +450 -367
- package/src/agents/jules/pulse.js +10 -327
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +309 -308
- package/src/agents/jules/tools/aidenid-email.js +189 -0
- package/src/agents/jules/tools/auth-audit.js +1691 -557
- package/src/agents/jules/tools/dispatch.js +335 -327
- package/src/agents/jules/tools/file-edit.js +2 -180
- package/src/agents/jules/tools/file-read.js +2 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -168
- package/src/agents/jules/tools/grep.js +2 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -161
- package/src/agents/jules/tools/runtime-audit.js +507 -503
- package/src/agents/jules/tools/shell.js +2 -383
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/persona-visuals.js +61 -0
- package/src/agents/shared-tools/dispatch-core.js +315 -0
- package/src/agents/shared-tools/file-edit.js +180 -0
- package/src/agents/shared-tools/file-read.js +100 -0
- package/src/agents/shared-tools/glob.js +168 -0
- package/src/agents/shared-tools/grep.js +228 -0
- package/src/agents/shared-tools/index.js +46 -0
- package/src/agents/shared-tools/path-guards.js +161 -0
- package/src/agents/shared-tools/shell.js +383 -0
- package/src/ai/aidenid.js +1009 -972
- package/src/ai/client.js +553 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -0
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +371 -126
- package/src/auth/http.js +611 -270
- package/src/auth/service.js +1106 -891
- package/src/auth/session-store.js +813 -359
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +419 -375
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +29 -21
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +872 -866
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/fix-cycle.js +377 -0
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -0
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/interactive/index.js +97 -95
- package/src/legacy-cli.js +2994 -2592
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -118
- package/src/review/ai-review.js +679 -669
- package/src/review/local-review.js +1305 -1295
- package/src/review/omargate-interactive.js +68 -0
- package/src/review/omargate-orchestrator.js +300 -0
- package/src/review/persona-prompts.js +296 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/scan-modes.js +42 -0
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -118
- package/src/telemetry/sync.js +203 -199
- package/src/ui/command-hints.js +13 -0
- package/src/ui/markdown.js +220 -220
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive post-scan menu for Omar Gate deep-dive.
|
|
3
|
+
*
|
|
4
|
+
* After the scan completes, prompts the user to select a domain agent
|
|
5
|
+
* for full agentic loop analysis (multi-turn, tool-using).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createInterface } from "node:readline/promises";
|
|
9
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
10
|
+
import pc from "picocolors";
|
|
11
|
+
|
|
12
|
+
import { PERSONA_IDS } from "./persona-prompts.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Show interactive persona selection after Omar Gate scan.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} options
|
|
18
|
+
* @param {object} options.scanResult - Result from runOmarGateOrchestrator
|
|
19
|
+
* @returns {Promise<string|null>} Selected persona ID, "all", or null (skip)
|
|
20
|
+
*/
|
|
21
|
+
export async function promptPersonaDeepDive({ scanResult } = {}) {
|
|
22
|
+
const summary = scanResult?.summary || {};
|
|
23
|
+
const personas = scanResult?.personas || [];
|
|
24
|
+
|
|
25
|
+
console.log("");
|
|
26
|
+
console.log(pc.bold("Omar Gate scan complete."));
|
|
27
|
+
console.log(
|
|
28
|
+
`Findings: P0=${summary.P0 || 0} P1=${summary.P1 || 0} P2=${summary.P2 || 0} P3=${summary.P3 || 0} | ` +
|
|
29
|
+
`Cost: $${(scanResult?.totalCostUsd || 0).toFixed(4)} | ` +
|
|
30
|
+
`Duration: ${((scanResult?.totalDurationMs || 0) / 1000).toFixed(1)}s`
|
|
31
|
+
);
|
|
32
|
+
console.log("");
|
|
33
|
+
|
|
34
|
+
// Show persona results
|
|
35
|
+
for (const p of personas) {
|
|
36
|
+
const icon = p.status === "ok" ? pc.green("✓") : p.status === "skipped" ? pc.gray("○") : pc.red("✗");
|
|
37
|
+
const count = p.findings || 0;
|
|
38
|
+
console.log(` ${icon} ${p.id} — ${count} finding${count === 1 ? "" : "s"}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log(pc.gray("Deep-dive runs a full agentic loop (multi-turn, tool-using) for deeper analysis."));
|
|
43
|
+
console.log(pc.gray(`Available: ${PERSONA_IDS.join(", ")}, all, none`));
|
|
44
|
+
console.log("");
|
|
45
|
+
|
|
46
|
+
const rl = createInterface({ input, output });
|
|
47
|
+
try {
|
|
48
|
+
const answer = await rl.question(pc.cyan("Deep-dive into which agent? [none] "));
|
|
49
|
+
const normalized = String(answer || "").trim().toLowerCase();
|
|
50
|
+
|
|
51
|
+
if (!normalized || normalized === "none" || normalized === "n" || normalized === "skip") {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (normalized === "all") {
|
|
56
|
+
return "all";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (PERSONA_IDS.includes(normalized)) {
|
|
60
|
+
return normalized;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(pc.yellow(`Unknown agent '${normalized}'. Skipping deep-dive.`));
|
|
64
|
+
return null;
|
|
65
|
+
} finally {
|
|
66
|
+
rl.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omar Gate multi-persona orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Runs N persona-scoped AI review calls in parallel (bounded concurrency),
|
|
5
|
+
* merges findings into a blackboard, deduplicates, and produces a unified report.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
|
|
10
|
+
import { runAiReviewLayer } from "./ai-review.js";
|
|
11
|
+
import { buildPersonaReviewPrompt, PERSONA_IDS } from "./persona-prompts.js";
|
|
12
|
+
import { resolveScanMode } from "./scan-modes.js";
|
|
13
|
+
import { syncRunToDashboard } from "../telemetry/sync.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run bounded-concurrency parallel execution.
|
|
17
|
+
* @param {Array} items
|
|
18
|
+
* @param {number} maxConcurrent
|
|
19
|
+
* @param {Function} fn
|
|
20
|
+
* @returns {Promise<Array>}
|
|
21
|
+
*/
|
|
22
|
+
async function runWithConcurrency(items, maxConcurrent, fn) {
|
|
23
|
+
const results = [];
|
|
24
|
+
const executing = new Set();
|
|
25
|
+
|
|
26
|
+
for (const item of items) {
|
|
27
|
+
const p = fn(item).then((result) => {
|
|
28
|
+
executing.delete(p);
|
|
29
|
+
return result;
|
|
30
|
+
});
|
|
31
|
+
executing.add(p);
|
|
32
|
+
results.push(p);
|
|
33
|
+
|
|
34
|
+
if (executing.size >= maxConcurrent) {
|
|
35
|
+
await Promise.race(executing);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return Promise.allSettled(results);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Deduplicate findings by file:line:title key.
|
|
44
|
+
*/
|
|
45
|
+
function deduplicateFindings(allFindings) {
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const unique = [];
|
|
48
|
+
for (const finding of allFindings) {
|
|
49
|
+
const key = `${finding.file || ""}:${finding.line || 0}:${String(finding.title || finding.message || "").toLowerCase().slice(0, 80)}`;
|
|
50
|
+
if (seen.has(key)) continue;
|
|
51
|
+
seen.add(key);
|
|
52
|
+
unique.push(finding);
|
|
53
|
+
}
|
|
54
|
+
return unique;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run the Omar Gate multi-persona orchestrator.
|
|
59
|
+
*
|
|
60
|
+
* @param {object} options
|
|
61
|
+
* @param {string} options.targetPath - Repository path
|
|
62
|
+
* @param {string} [options.scanMode] - "baseline", "deep", or "full-depth"
|
|
63
|
+
* @param {number} [options.maxParallel] - Max concurrent persona calls (default 4)
|
|
64
|
+
* @param {string} [options.provider] - LLM provider override
|
|
65
|
+
* @param {string} [options.model] - LLM model override
|
|
66
|
+
* @param {number} [options.maxCostUsd] - Global cost ceiling (default 5.0)
|
|
67
|
+
* @param {boolean} [options.dryRun] - Dry-run mode (no LLM calls)
|
|
68
|
+
* @param {string} [options.outputDir] - Output directory override
|
|
69
|
+
* @param {object} [options.deterministic] - Deterministic scan results
|
|
70
|
+
* @param {Function} [options.onEvent] - Event callback for streaming
|
|
71
|
+
* @returns {Promise<object>} Orchestrated results
|
|
72
|
+
*/
|
|
73
|
+
export async function runOmarGateOrchestrator({
|
|
74
|
+
targetPath,
|
|
75
|
+
scanMode = "deep",
|
|
76
|
+
maxParallel = 4,
|
|
77
|
+
provider = "",
|
|
78
|
+
model = "",
|
|
79
|
+
maxCostUsd = 5.0,
|
|
80
|
+
dryRun = false,
|
|
81
|
+
outputDir = "",
|
|
82
|
+
deterministic = null,
|
|
83
|
+
onEvent = null,
|
|
84
|
+
} = {}) {
|
|
85
|
+
const runId = `omargate-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
86
|
+
const startTime = Date.now();
|
|
87
|
+
|
|
88
|
+
const { mode, personas } = resolveScanMode(scanMode);
|
|
89
|
+
|
|
90
|
+
if (onEvent) {
|
|
91
|
+
onEvent({
|
|
92
|
+
stream: "sl_event",
|
|
93
|
+
event: "omargate_start",
|
|
94
|
+
payload: { runId, mode, personas, maxParallel, maxCostUsd, dryRun },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const detSummary = deterministic?.summary || { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
99
|
+
const detFindings = deterministic?.findings || [];
|
|
100
|
+
|
|
101
|
+
// Per-persona cost budget = global / persona count (with minimum floor)
|
|
102
|
+
const perPersonaCost = Math.max(0.25, maxCostUsd / personas.length);
|
|
103
|
+
let runningCostUsd = 0;
|
|
104
|
+
|
|
105
|
+
const personaResults = await runWithConcurrency(personas, maxParallel, async (personaId) => {
|
|
106
|
+
// Global budget check — skip remaining personas if exhausted
|
|
107
|
+
if (runningCostUsd >= maxCostUsd) {
|
|
108
|
+
if (onEvent) {
|
|
109
|
+
onEvent({
|
|
110
|
+
stream: "sl_event",
|
|
111
|
+
event: "persona_skipped",
|
|
112
|
+
payload: { personaId, reason: "global_budget_exhausted", runningCostUsd, maxCostUsd },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
personaId,
|
|
117
|
+
status: "skipped",
|
|
118
|
+
findings: [],
|
|
119
|
+
summary: { P0: 0, P1: 0, P2: 0, P3: 0 },
|
|
120
|
+
costUsd: 0,
|
|
121
|
+
durationMs: 0,
|
|
122
|
+
reason: "global_budget_exhausted",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const personaStart = Date.now();
|
|
127
|
+
|
|
128
|
+
if (onEvent) {
|
|
129
|
+
onEvent({
|
|
130
|
+
stream: "sl_event",
|
|
131
|
+
event: "persona_start",
|
|
132
|
+
payload: { personaId, mode, runId },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const systemPrompt = buildPersonaReviewPrompt({
|
|
138
|
+
personaId,
|
|
139
|
+
targetPath,
|
|
140
|
+
deterministicSummary: detSummary,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = await runAiReviewLayer({
|
|
144
|
+
targetPath,
|
|
145
|
+
mode: "full",
|
|
146
|
+
runId: `${runId}-${personaId}`,
|
|
147
|
+
runDirectory: targetPath,
|
|
148
|
+
deterministic: {
|
|
149
|
+
summary: detSummary,
|
|
150
|
+
findings: detFindings,
|
|
151
|
+
metadata: deterministic?.metadata || {},
|
|
152
|
+
},
|
|
153
|
+
outputDir,
|
|
154
|
+
provider: provider || undefined,
|
|
155
|
+
model: model || undefined,
|
|
156
|
+
maxCostUsd: perPersonaCost,
|
|
157
|
+
dryRun,
|
|
158
|
+
env: process.env,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const findings = (result?.findings || []).map((f) => ({
|
|
162
|
+
...f,
|
|
163
|
+
persona: personaId,
|
|
164
|
+
layer: personaId,
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
if (onEvent) {
|
|
168
|
+
for (const finding of findings) {
|
|
169
|
+
onEvent({
|
|
170
|
+
stream: "sl_event",
|
|
171
|
+
event: "persona_finding",
|
|
172
|
+
payload: { personaId, ...finding },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
onEvent({
|
|
176
|
+
stream: "sl_event",
|
|
177
|
+
event: "persona_complete",
|
|
178
|
+
payload: {
|
|
179
|
+
personaId,
|
|
180
|
+
findings: findings.length,
|
|
181
|
+
summary: result?.summary || {},
|
|
182
|
+
costUsd: result?.costUsd || 0,
|
|
183
|
+
durationMs: Date.now() - personaStart,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const personaCost = result?.costUsd || 0;
|
|
189
|
+
runningCostUsd += personaCost;
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
personaId,
|
|
193
|
+
status: "ok",
|
|
194
|
+
findings,
|
|
195
|
+
summary: result?.summary || { P0: 0, P1: 0, P2: 0, P3: 0 },
|
|
196
|
+
costUsd: personaCost,
|
|
197
|
+
model: result?.model || model || null,
|
|
198
|
+
durationMs: Date.now() - personaStart,
|
|
199
|
+
};
|
|
200
|
+
} catch (err) {
|
|
201
|
+
if (onEvent) {
|
|
202
|
+
onEvent({
|
|
203
|
+
stream: "sl_event",
|
|
204
|
+
event: "persona_error",
|
|
205
|
+
payload: { personaId, error: err.message },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
personaId,
|
|
210
|
+
status: "error",
|
|
211
|
+
findings: [],
|
|
212
|
+
summary: { P0: 0, P1: 0, P2: 0, P3: 0 },
|
|
213
|
+
costUsd: 0,
|
|
214
|
+
error: err.message,
|
|
215
|
+
durationMs: Date.now() - personaStart,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Collect results (handle settled promises)
|
|
221
|
+
const settled = personaResults.map((r) =>
|
|
222
|
+
r.status === "fulfilled" ? r.value : { personaId: "unknown", status: "error", findings: [], summary: { P0: 0, P1: 0, P2: 0, P3: 0 }, costUsd: 0, error: r.reason?.message || "unknown" }
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Merge and deduplicate findings
|
|
226
|
+
const allAiFindings = settled.flatMap((r) => r.findings);
|
|
227
|
+
const uniqueFindings = deduplicateFindings(allAiFindings);
|
|
228
|
+
|
|
229
|
+
// Compute combined summary
|
|
230
|
+
const combinedP0 = uniqueFindings.filter((f) => f.severity === "P0").length;
|
|
231
|
+
const combinedP1 = uniqueFindings.filter((f) => f.severity === "P1").length;
|
|
232
|
+
const combinedP2 = uniqueFindings.filter((f) => f.severity === "P2").length;
|
|
233
|
+
const combinedP3 = uniqueFindings.filter((f) => f.severity === "P3").length;
|
|
234
|
+
const totalCost = settled.reduce((sum, r) => sum + (r.costUsd || 0), 0);
|
|
235
|
+
const totalDuration = Date.now() - startTime;
|
|
236
|
+
|
|
237
|
+
const result = {
|
|
238
|
+
runId,
|
|
239
|
+
mode,
|
|
240
|
+
personas: settled.map((r) => ({
|
|
241
|
+
id: r.personaId,
|
|
242
|
+
status: r.status,
|
|
243
|
+
findings: r.findings.length,
|
|
244
|
+
costUsd: r.costUsd,
|
|
245
|
+
durationMs: r.durationMs,
|
|
246
|
+
error: r.error || null,
|
|
247
|
+
})),
|
|
248
|
+
findings: uniqueFindings,
|
|
249
|
+
summary: {
|
|
250
|
+
P0: combinedP0,
|
|
251
|
+
P1: combinedP1,
|
|
252
|
+
P2: combinedP2,
|
|
253
|
+
P3: combinedP3,
|
|
254
|
+
blocking: combinedP0 > 0 || combinedP1 > 0,
|
|
255
|
+
},
|
|
256
|
+
totalCostUsd: totalCost,
|
|
257
|
+
totalDurationMs: totalDuration,
|
|
258
|
+
dryRun,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
if (onEvent) {
|
|
262
|
+
onEvent({
|
|
263
|
+
stream: "sl_event",
|
|
264
|
+
event: "omargate_complete",
|
|
265
|
+
payload: {
|
|
266
|
+
runId,
|
|
267
|
+
mode,
|
|
268
|
+
personaCount: settled.length,
|
|
269
|
+
findings: uniqueFindings.length,
|
|
270
|
+
summary: result.summary,
|
|
271
|
+
totalCostUsd: totalCost,
|
|
272
|
+
totalDurationMs: totalDuration,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Fire-and-forget telemetry sync to dashboard
|
|
278
|
+
syncRunToDashboard({
|
|
279
|
+
command: `omargate deep --scan-mode ${mode}`,
|
|
280
|
+
persona: "omar-orchestrator",
|
|
281
|
+
usage: {
|
|
282
|
+
inputTokens: 0,
|
|
283
|
+
outputTokens: 0,
|
|
284
|
+
costUsd: totalCost,
|
|
285
|
+
durationMs: totalDuration,
|
|
286
|
+
toolCalls: personas.length,
|
|
287
|
+
},
|
|
288
|
+
summary: result.summary,
|
|
289
|
+
stopReason: result.summary.blocking ? "blocked" : "passed",
|
|
290
|
+
personaBreakdown: settled.map((r) => ({
|
|
291
|
+
personaId: r.personaId,
|
|
292
|
+
findings: r.findings?.length || 0,
|
|
293
|
+
costUsd: r.costUsd || 0,
|
|
294
|
+
durationMs: r.durationMs || 0,
|
|
295
|
+
status: r.status,
|
|
296
|
+
})),
|
|
297
|
+
}).catch(() => {});
|
|
298
|
+
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persona-scoped system prompts for Omar Gate AI analysis.
|
|
3
|
+
*
|
|
4
|
+
* Each persona gets a domain-focused prompt that constrains the LLM
|
|
5
|
+
* to analyze code through a specific security/quality lens.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const PERSONA_PROMPTS = {
|
|
9
|
+
security: {
|
|
10
|
+
role: "Nina Patel — Security Specialist",
|
|
11
|
+
focus: `You are a security specialist reviewing code for exploitable vulnerabilities.
|
|
12
|
+
|
|
13
|
+
Focus areas:
|
|
14
|
+
- Authentication and authorization bypass paths
|
|
15
|
+
- Secret/credential exposure in code, configs, logs, and environment
|
|
16
|
+
- Injection vectors: SQL, shell, XSS, SSRF, path traversal
|
|
17
|
+
- Cryptographic weaknesses: weak hashing, hardcoded keys, insecure TLS
|
|
18
|
+
- Session management: fixation, token leakage, cookie misconfiguration
|
|
19
|
+
- Rate limiting gaps on auth and payment endpoints
|
|
20
|
+
- CORS misconfiguration allowing unauthorized origins
|
|
21
|
+
- Insecure deserialization and dynamic code execution (eval, Function)
|
|
22
|
+
|
|
23
|
+
Evidence standard: Every finding MUST include file:line, exploit scenario, and remediation.
|
|
24
|
+
Do NOT report hypothetical issues without concrete code evidence.`,
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
architecture: {
|
|
28
|
+
role: "Maya Volkov — Architecture Specialist",
|
|
29
|
+
focus: `You are an architecture specialist reviewing code for structural quality.
|
|
30
|
+
|
|
31
|
+
Focus areas:
|
|
32
|
+
- God components/modules (>300 LOC, >10 responsibilities)
|
|
33
|
+
- Circular dependencies between modules
|
|
34
|
+
- Tight coupling between layers (presentation → data access)
|
|
35
|
+
- Missing abstraction boundaries (business logic in route handlers)
|
|
36
|
+
- State management sprawl (>15 useState in a component)
|
|
37
|
+
- Missing error boundaries and fallback handling
|
|
38
|
+
- Inconsistent naming/organization patterns
|
|
39
|
+
- Dead code and unreachable paths
|
|
40
|
+
|
|
41
|
+
Evidence standard: Every finding MUST include file:line, coupling graph or complexity metric, and refactoring guidance.`,
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
testing: {
|
|
45
|
+
role: "Priya Raman — Testing Specialist",
|
|
46
|
+
focus: `You are a testing specialist reviewing code for coverage gaps and test quality.
|
|
47
|
+
|
|
48
|
+
Focus areas:
|
|
49
|
+
- Critical paths without test coverage (auth, payment, data mutation)
|
|
50
|
+
- Tests that mock too much (false confidence)
|
|
51
|
+
- Missing edge case tests (empty inputs, boundary values, error paths)
|
|
52
|
+
- Flaky test patterns (timing, external dependencies, shared state)
|
|
53
|
+
- Missing integration tests for API endpoints
|
|
54
|
+
- No E2E tests for critical user flows
|
|
55
|
+
- Test data that doesn't represent production scenarios
|
|
56
|
+
- Missing assertion specificity (assertTrue vs assertEquals)
|
|
57
|
+
|
|
58
|
+
Evidence standard: Every finding MUST include the untested code path (file:line) and a concrete test case outline.`,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
performance: {
|
|
62
|
+
role: "Arjun Mehta — Performance Specialist",
|
|
63
|
+
focus: `You are a performance specialist reviewing code for latency and efficiency issues.
|
|
64
|
+
|
|
65
|
+
Focus areas:
|
|
66
|
+
- N+1 query patterns (loop-based database calls)
|
|
67
|
+
- Missing database indexes on WHERE/JOIN/ORDER BY columns
|
|
68
|
+
- Unbounded data fetching (no LIMIT, no pagination)
|
|
69
|
+
- Synchronous blocking in async contexts
|
|
70
|
+
- Memory leaks (unclosed connections, event listeners, timers)
|
|
71
|
+
- Bundle size bloat (large imports, no tree shaking, no code splitting)
|
|
72
|
+
- Missing caching for expensive computations
|
|
73
|
+
- Render performance (unnecessary re-renders, missing memoization)
|
|
74
|
+
|
|
75
|
+
Evidence standard: Every finding MUST include file:line, estimated performance impact, and optimization approach.`,
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
compliance: {
|
|
79
|
+
role: "Leila Farouk — Compliance Specialist",
|
|
80
|
+
focus: `You are a compliance specialist reviewing code for regulatory adherence.
|
|
81
|
+
|
|
82
|
+
Focus areas:
|
|
83
|
+
- PII handling without encryption or access controls
|
|
84
|
+
- Missing audit logging for data access and mutations
|
|
85
|
+
- GDPR: data retention without deletion mechanisms
|
|
86
|
+
- SOC2: missing access controls, no principle of least privilege
|
|
87
|
+
- HIPAA: PHI exposure, missing BAA requirements
|
|
88
|
+
- Missing consent tracking for data collection
|
|
89
|
+
- Insecure data export/download without authorization
|
|
90
|
+
- Missing data classification and sensitivity labels
|
|
91
|
+
|
|
92
|
+
Evidence standard: Every finding MUST include the regulatory requirement, the gap, and the remediation with compliance evidence.`,
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
documentation: {
|
|
96
|
+
role: "Samir Okafor — Documentation Specialist",
|
|
97
|
+
focus: `You are a documentation specialist reviewing for operational clarity.
|
|
98
|
+
|
|
99
|
+
Focus areas:
|
|
100
|
+
- Missing or outdated README/setup instructions
|
|
101
|
+
- API endpoints without documentation
|
|
102
|
+
- Missing runbooks for incident response
|
|
103
|
+
- Configuration options without documentation
|
|
104
|
+
- Missing architecture decision records (ADRs)
|
|
105
|
+
- Outdated deployment instructions
|
|
106
|
+
- Missing onboarding documentation for new developers
|
|
107
|
+
|
|
108
|
+
Evidence standard: Every finding MUST include what is missing, where it should live, and a draft outline.`,
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
reliability: {
|
|
112
|
+
role: "Noah Ben-David — Reliability Specialist",
|
|
113
|
+
focus: `You are a reliability specialist reviewing code for fault tolerance.
|
|
114
|
+
|
|
115
|
+
Focus areas:
|
|
116
|
+
- Missing timeout configuration on external calls
|
|
117
|
+
- No retry logic or exponential backoff for transient failures
|
|
118
|
+
- Missing circuit breakers on external service calls
|
|
119
|
+
- No graceful degradation when dependencies are down
|
|
120
|
+
- Missing health check endpoints
|
|
121
|
+
- Queue backpressure handling gaps
|
|
122
|
+
- Missing dead letter queue for failed jobs
|
|
123
|
+
- No idempotency keys on mutation endpoints
|
|
124
|
+
|
|
125
|
+
Evidence standard: Every finding MUST include the failure scenario, blast radius, and resilience pattern to apply.`,
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
release: {
|
|
129
|
+
role: "Omar Singh — Release Engineering Specialist",
|
|
130
|
+
focus: `You are a release engineering specialist reviewing CI/CD and deployment.
|
|
131
|
+
|
|
132
|
+
Focus areas:
|
|
133
|
+
- Unpinned GitHub Actions (using @main instead of SHA)
|
|
134
|
+
- Missing artifact signing or provenance attestation
|
|
135
|
+
- No rollback mechanism in deployment pipeline
|
|
136
|
+
- Missing smoke tests after deploy
|
|
137
|
+
- Secrets in CI/CD logs or artifacts
|
|
138
|
+
- Missing branch protection rules
|
|
139
|
+
- No canary or staged rollout strategy
|
|
140
|
+
- Deploy pipeline without quality gates
|
|
141
|
+
|
|
142
|
+
Evidence standard: Every finding MUST include the workflow file:line, risk, and the hardened alternative.`,
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
observability: {
|
|
146
|
+
role: "Sofia Alvarez — Observability Specialist",
|
|
147
|
+
focus: `You are an observability specialist reviewing telemetry and alerting.
|
|
148
|
+
|
|
149
|
+
Focus areas:
|
|
150
|
+
- Missing structured logging (console.log without context)
|
|
151
|
+
- No request tracing (missing correlation IDs)
|
|
152
|
+
- Missing error tracking integration
|
|
153
|
+
- No alerting on error rate spikes
|
|
154
|
+
- Missing latency tracking on critical paths
|
|
155
|
+
- No dashboard for key business metrics
|
|
156
|
+
- Missing SLO/SLI definitions
|
|
157
|
+
- Blind spots: operations without any telemetry
|
|
158
|
+
|
|
159
|
+
Evidence standard: Every finding MUST include what metric/signal is missing, where to instrument, and the alert threshold.`,
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
infrastructure: {
|
|
163
|
+
role: "Kat Hughes — Infrastructure Specialist",
|
|
164
|
+
focus: `You are an infrastructure specialist reviewing cloud and deployment config.
|
|
165
|
+
|
|
166
|
+
Focus areas:
|
|
167
|
+
- Overly permissive IAM policies (wildcard actions/resources)
|
|
168
|
+
- Public-facing resources without WAF/rate limiting
|
|
169
|
+
- Missing encryption at rest or in transit
|
|
170
|
+
- Hardcoded infrastructure values (IPs, ARNs, account IDs)
|
|
171
|
+
- Missing VPC/subnet isolation
|
|
172
|
+
- No secrets rotation policy
|
|
173
|
+
- Missing backup and disaster recovery configuration
|
|
174
|
+
- Infrastructure drift (manual changes not in IaC)
|
|
175
|
+
|
|
176
|
+
Evidence standard: Every finding MUST include the resource, the misconfiguration, blast radius, and the IaC fix.`,
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
"supply-chain": {
|
|
180
|
+
role: "Nora Kline — Supply Chain Specialist",
|
|
181
|
+
focus: `You are a supply chain specialist reviewing dependency security.
|
|
182
|
+
|
|
183
|
+
Focus areas:
|
|
184
|
+
- Dependencies with known CVEs (critical/high severity)
|
|
185
|
+
- Unpinned dependency versions (using ^/~ instead of exact)
|
|
186
|
+
- Dependencies from untrusted or abandoned packages
|
|
187
|
+
- Missing lockfile integrity checks
|
|
188
|
+
- No SBOM generation in build pipeline
|
|
189
|
+
- Typosquatting risk (similar package names)
|
|
190
|
+
- Excessive dependency tree depth
|
|
191
|
+
- Missing license compliance checks
|
|
192
|
+
|
|
193
|
+
Evidence standard: Every finding MUST include the package name, version, CVE/risk, and the pinned/patched alternative.`,
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
frontend: {
|
|
197
|
+
role: "Jules Tanaka — Frontend Specialist",
|
|
198
|
+
focus: `You are a frontend specialist reviewing UI code for production readiness.
|
|
199
|
+
|
|
200
|
+
Focus areas:
|
|
201
|
+
- XSS via dangerouslySetInnerHTML without sanitization
|
|
202
|
+
- Client-side token storage in localStorage (use httpOnly cookies)
|
|
203
|
+
- Missing input validation on forms
|
|
204
|
+
- Accessibility failures (missing alt text, labels, keyboard navigation)
|
|
205
|
+
- Bundle size > 200KB initial JS
|
|
206
|
+
- Missing error boundaries around route components
|
|
207
|
+
- CLS-causing patterns (images without dimensions, dynamic content injection)
|
|
208
|
+
- Missing loading/error states on data fetching
|
|
209
|
+
|
|
210
|
+
Evidence standard: Every finding MUST include file:line, user impact, and the specific fix.`,
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
"ai-governance": {
|
|
214
|
+
role: "Amina Chen — AI Governance Specialist",
|
|
215
|
+
focus: `You are an AI governance specialist reviewing AI/ML code safety.
|
|
216
|
+
|
|
217
|
+
Focus areas:
|
|
218
|
+
- Prompt injection vectors in user-facing LLM prompts
|
|
219
|
+
- Missing input sanitization before LLM calls
|
|
220
|
+
- No rate limiting on AI endpoints
|
|
221
|
+
- Missing cost/token budget enforcement
|
|
222
|
+
- No human-in-the-loop for high-risk AI decisions
|
|
223
|
+
- Missing model versioning and eval regression checks
|
|
224
|
+
- Tool/agent permission escalation risks
|
|
225
|
+
- Missing audit trail for AI-generated actions
|
|
226
|
+
|
|
227
|
+
Evidence standard: Every finding MUST include the injection/bypass scenario, the affected code path, and the guardrail to add.`,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build a persona-scoped system prompt for Omar Gate AI analysis.
|
|
233
|
+
*
|
|
234
|
+
* @param {object} options
|
|
235
|
+
* @param {string} options.personaId - Agent ID (e.g., "security", "architecture")
|
|
236
|
+
* @param {string} [options.targetPath] - Repository path
|
|
237
|
+
* @param {object} [options.deterministicSummary] - Summary from deterministic scan
|
|
238
|
+
* @param {number} [options.maxFindings] - Max findings to return (default 20)
|
|
239
|
+
* @returns {string} System prompt
|
|
240
|
+
*/
|
|
241
|
+
export function buildPersonaReviewPrompt({
|
|
242
|
+
personaId,
|
|
243
|
+
targetPath = "",
|
|
244
|
+
deterministicSummary = {},
|
|
245
|
+
maxFindings = 20,
|
|
246
|
+
} = {}) {
|
|
247
|
+
const persona = PERSONA_PROMPTS[personaId];
|
|
248
|
+
if (!persona) {
|
|
249
|
+
return buildGenericPrompt({ targetPath, deterministicSummary, maxFindings });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return `# ${persona.role}
|
|
253
|
+
|
|
254
|
+
${persona.focus}
|
|
255
|
+
|
|
256
|
+
## Context
|
|
257
|
+
Target: ${targetPath || "(not provided)"}
|
|
258
|
+
Deterministic scan: P0=${deterministicSummary.P0 || 0} P1=${deterministicSummary.P1 || 0} P2=${deterministicSummary.P2 || 0} P3=${deterministicSummary.P3 || 0}
|
|
259
|
+
|
|
260
|
+
## Output Contract
|
|
261
|
+
Return a JSON array of findings. Maximum ${maxFindings} findings. Each finding:
|
|
262
|
+
\`\`\`json
|
|
263
|
+
{
|
|
264
|
+
"severity": "P0|P1|P2|P3",
|
|
265
|
+
"file": "path/to/file.ext",
|
|
266
|
+
"line": 42,
|
|
267
|
+
"title": "Brief description",
|
|
268
|
+
"evidence": "Concrete code evidence at file:line",
|
|
269
|
+
"rootCause": "Why this is a problem",
|
|
270
|
+
"recommendedFix": "Specific fix to apply",
|
|
271
|
+
"confidence": 0.85
|
|
272
|
+
}
|
|
273
|
+
\`\`\`
|
|
274
|
+
|
|
275
|
+
Rules:
|
|
276
|
+
- Only report findings you have HIGH confidence in (>= 0.7)
|
|
277
|
+
- Every finding MUST have concrete file:line evidence
|
|
278
|
+
- Do NOT repeat findings already in the deterministic scan
|
|
279
|
+
- Do NOT report hypothetical/speculative issues
|
|
280
|
+
- Focus on REAL, EXPLOITABLE, IMPACTFUL problems in your domain
|
|
281
|
+
- Return ONLY the JSON array, no other text
|
|
282
|
+
`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildGenericPrompt({ targetPath, deterministicSummary, maxFindings }) {
|
|
286
|
+
return `You are a senior code reviewer. Analyze the code for security, quality, and reliability issues.
|
|
287
|
+
|
|
288
|
+
Target: ${targetPath || "(not provided)"}
|
|
289
|
+
Deterministic scan: P0=${deterministicSummary.P0 || 0} P1=${deterministicSummary.P1 || 0} P2=${deterministicSummary.P2 || 0}
|
|
290
|
+
|
|
291
|
+
Return a JSON array of up to ${maxFindings} findings with: severity, file, line, title, evidence, rootCause, recommendedFix, confidence.
|
|
292
|
+
Only report findings with concrete evidence. Do NOT repeat deterministic findings.`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const PERSONA_IDS = Object.keys(PERSONA_PROMPTS);
|
|
296
|
+
export { PERSONA_PROMPTS };
|