sentinelayer-cli 0.8.11 → 0.9.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.
Files changed (38) hide show
  1. package/package.json +10 -5
  2. package/src/agents/devtestbot/config/definition.js +100 -0
  3. package/src/agents/devtestbot/config/system-prompt.js +92 -0
  4. package/src/agents/devtestbot/index.js +9 -0
  5. package/src/agents/devtestbot/runner.js +769 -0
  6. package/src/agents/devtestbot/tool.js +707 -0
  7. package/src/agents/jules/stream.js +2 -12
  8. package/src/audit/orchestrator.js +471 -114
  9. package/src/audit/persona-loop.js +1342 -0
  10. package/src/audit/registry.js +58 -2
  11. package/src/commands/audit.js +42 -1
  12. package/src/commands/legacy-args.js +32 -1
  13. package/src/commands/omargate.js +4 -0
  14. package/src/commands/session.js +417 -89
  15. package/src/commands/swarm.js +11 -2
  16. package/src/cost/history.js +41 -21
  17. package/src/events/schema.js +27 -1
  18. package/src/guide/generator.js +14 -0
  19. package/src/legacy-cli.js +110 -18
  20. package/src/prompt/generator.js +4 -16
  21. package/src/review/ai-review.js +95 -6
  22. package/src/review/dd-report-email-client.js +148 -0
  23. package/src/review/investor-dd-devtestbot.js +599 -0
  24. package/src/review/investor-dd-orchestrator.js +135 -3
  25. package/src/review/omargate-cache.js +285 -0
  26. package/src/review/omargate-orchestrator.js +605 -4
  27. package/src/review/persona-prompts.js +34 -1
  28. package/src/review/report.js +189 -4
  29. package/src/session/coordination-guidance.js +48 -0
  30. package/src/session/daemon.js +3 -2
  31. package/src/session/listener.js +236 -0
  32. package/src/session/senti-naming.js +36 -0
  33. package/src/session/setup-guides.js +3 -15
  34. package/src/session/store.js +54 -5
  35. package/src/session/sync.js +23 -0
  36. package/src/spec/generator.js +8 -10
  37. package/src/swarm/registry.js +20 -0
  38. package/src/swarm/runtime.js +139 -1
@@ -1,6 +1,28 @@
1
1
  import fsp from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
+ export const DEFAULT_AUDIT_AGENT_TOOLS = Object.freeze([
5
+ "FileRead",
6
+ "Grep",
7
+ "Glob",
8
+ "Shell",
9
+ "FileEdit",
10
+ ]);
11
+
12
+ const TOOL_NAME_ALIASES = Object.freeze({
13
+ read: "FileRead",
14
+ file_read: "FileRead",
15
+ "file-read": "FileRead",
16
+ fileread: "FileRead",
17
+ grep: "Grep",
18
+ glob: "Glob",
19
+ shell: "Shell",
20
+ file_edit: "FileEdit",
21
+ "file-edit": "FileEdit",
22
+ fileedit: "FileEdit",
23
+ dispatch: "Dispatch",
24
+ });
25
+
4
26
  const BUILTIN_AUDIT_AGENTS = Object.freeze([
5
27
  {
6
28
  id: "security",
@@ -173,16 +195,40 @@ function normalizeString(value) {
173
195
  return String(value || "").trim();
174
196
  }
175
197
 
198
+ function normalizeToolName(value) {
199
+ const raw = normalizeString(value);
200
+ if (!raw) {
201
+ return "";
202
+ }
203
+ const alias = TOOL_NAME_ALIASES[raw.toLowerCase()];
204
+ if (alias) {
205
+ return alias;
206
+ }
207
+ return raw;
208
+ }
209
+
210
+ export function normalizeAuditAgentTools(tools = [], { useDefaultWhenEmpty = false } = {}) {
211
+ const normalized = Array.isArray(tools)
212
+ ? tools.map((item) => normalizeToolName(item)).filter(Boolean)
213
+ : [];
214
+ const unique = [...new Set(normalized)];
215
+ if (unique.length > 0) {
216
+ return unique;
217
+ }
218
+ return useDefaultWhenEmpty ? [...DEFAULT_AUDIT_AGENT_TOOLS] : [];
219
+ }
220
+
176
221
  function normalizeAgentId(value) {
177
222
  return normalizeString(value).toLowerCase();
178
223
  }
179
224
 
180
225
  function normalizeAgentRecord(record = {}) {
226
+ const hasToolOverride = Object.prototype.hasOwnProperty.call(record, "tools");
181
227
  return {
182
228
  id: normalizeAgentId(record.id),
183
229
  persona: normalizeString(record.persona),
184
230
  domain: normalizeString(record.domain),
185
- tools: Array.isArray(record.tools) ? record.tools.map((item) => normalizeString(item)).filter(Boolean) : [],
231
+ tools: normalizeAuditAgentTools(record.tools, { useDefaultWhenEmpty: !hasToolOverride }),
186
232
  permissionMode: normalizeString(record.permissionMode || "plan") || "plan",
187
233
  maxTurns: Math.max(1, Math.floor(Number(record.maxTurns || 1))),
188
234
  confidenceFloor: Math.max(0, Math.min(1, Number(record.confidenceFloor || 0))),
@@ -206,6 +252,11 @@ function mergeRegistry(builtinAgents = [], overrideAgents = []) {
206
252
  continue;
207
253
  }
208
254
  const existing = byId.get(normalized.id) || {};
255
+ if (!Object.prototype.hasOwnProperty.call(override, "tools")) {
256
+ normalized.tools = Array.isArray(existing.tools) && existing.tools.length > 0
257
+ ? [...existing.tools]
258
+ : [...DEFAULT_AUDIT_AGENT_TOOLS];
259
+ }
209
260
  byId.set(normalized.id, {
210
261
  ...existing,
211
262
  ...normalized,
@@ -223,7 +274,12 @@ function parseAgentFilter(rawValue) {
223
274
  }
224
275
 
225
276
  export function listBuiltinAuditAgents() {
226
- return BUILTIN_AUDIT_AGENTS.map((agent) => ({ ...agent }));
277
+ return BUILTIN_AUDIT_AGENTS.map((agent) =>
278
+ normalizeAgentRecord({
279
+ ...agent,
280
+ tools: DEFAULT_AUDIT_AGENT_TOOLS,
281
+ })
282
+ );
227
283
  }
228
284
 
229
285
  export async function loadAuditRegistry({ registryFile = "" } = {}) {
@@ -26,6 +26,14 @@ function parseMaxParallel(rawValue) {
26
26
  return Math.floor(normalized);
27
27
  }
28
28
 
29
+ function parseIsolationMode(rawValue) {
30
+ const normalized = String(rawValue || "strict").trim().toLowerCase();
31
+ if (normalized === "strict" || normalized === "relaxed") {
32
+ return normalized;
33
+ }
34
+ throw new Error("isolation must be one of: strict, relaxed.");
35
+ }
36
+
29
37
  function printAuditSummary(result) {
30
38
  console.log(pc.bold("Audit orchestrator complete"));
31
39
  console.log(pc.gray(`Run: ${result.runId}`));
@@ -34,6 +42,11 @@ function printAuditSummary(result) {
34
42
  if (result.sharedMemory?.artifactPath) {
35
43
  console.log(pc.gray(`Shared memory: ${result.sharedMemory.artifactPath}`));
36
44
  }
45
+ if (result.omargateReuse?.used) {
46
+ console.log(pc.gray(`Reused OmarGate run: ${result.omargateReuse.runId}`));
47
+ } else if (result.omargateReuse?.requested) {
48
+ console.log(pc.gray(`OmarGate reuse unavailable: ${result.omargateReuse.reason || "not_found"}`));
49
+ }
37
50
  if (result.ddPackage?.executiveSummaryPath) {
38
51
  console.log(pc.gray(`DD package: ${result.ddPackage.executiveSummaryPath}`));
39
52
  }
@@ -51,6 +64,15 @@ function printAuditSummary(result) {
51
64
  console.log(`Agents: ${result.agentResults.length}, max_parallel=${result.maxParallel}`);
52
65
  }
53
66
 
67
+ function buildAuditOrchestratorEventHandler(emitStream) {
68
+ if (!emitStream) {
69
+ return null;
70
+ }
71
+ return (evt) => {
72
+ console.log(JSON.stringify(evt));
73
+ };
74
+ }
75
+
54
76
  export function registerAuditCommand(program, invokeLegacy) {
55
77
  const audit = program
56
78
  .command("audit")
@@ -63,9 +85,14 @@ export function registerAuditCommand(program, invokeLegacy) {
63
85
  .option("--output-dir <path>", "Optional artifact output root override")
64
86
  .option("--refresh", "Refresh CODEBASE_INGEST before running audit")
65
87
  .option("--dry-run", "Skip deterministic baseline and run orchestration planning only")
88
+ .option("--isolation <mode>", "Persona isolation mode: strict | relaxed", "strict")
89
+ .option("--no-seed-from-deterministic", "Run personas without deterministic baseline or specialist seed findings")
90
+ .option("--reuse-omargate <runId>", "Reuse deterministic findings from an OmarGate run id or latest")
91
+ .option("--stream", "Emit NDJSON agent events to stdout")
66
92
  .option("--json", "Emit machine-readable output")
67
93
  .action(async (targetPathArg, options, command) => {
68
94
  const emitJson = shouldEmitJson(options, command);
95
+ const emitStream = Boolean(options.stream);
69
96
  const targetPath = path.resolve(process.cwd(), String(options.path || targetPathArg || "."));
70
97
  const registry = await loadAuditRegistry({
71
98
  registryFile: options.registryFile,
@@ -85,6 +112,10 @@ export function registerAuditCommand(program, invokeLegacy) {
85
112
  outputDir: options.outputDir,
86
113
  dryRun: Boolean(options.dryRun),
87
114
  refreshIngest: Boolean(options.refresh),
115
+ isolation: parseIsolationMode(options.isolation),
116
+ seedFromDeterministic: options.seedFromDeterministic !== false,
117
+ reuseOmarGate: options.reuseOmargate,
118
+ onEvent: buildAuditOrchestratorEventHandler(emitStream),
88
119
  });
89
120
 
90
121
  const payload = {
@@ -99,6 +130,11 @@ export function registerAuditCommand(program, invokeLegacy) {
99
130
  registryFile: registry.registryFile,
100
131
  selectedAgents: result.selectedAgents,
101
132
  maxParallel: result.maxParallel,
133
+ isolation: result.isolation,
134
+ seedFromDeterministic: result.seedFromDeterministic !== false,
135
+ omargateReuse: result.omargateReuse || null,
136
+ reusedOmarGateRunId: result.omargateReuse?.used ? result.omargateReuse.runId : "",
137
+ reusedOmarGateDeterministicPath: result.omargateReuse?.used ? result.omargateReuse.artifactPath : "",
102
138
  summary: result.summary,
103
139
  agentCount: result.agentResults.length,
104
140
  sharedMemoryPath: result.sharedMemory?.artifactPath || "",
@@ -112,7 +148,7 @@ export function registerAuditCommand(program, invokeLegacy) {
112
148
 
113
149
  if (emitJson) {
114
150
  console.log(JSON.stringify(payload, null, 2));
115
- } else {
151
+ } else if (!emitStream) {
116
152
  printAuditSummary(result);
117
153
  }
118
154
 
@@ -211,6 +247,8 @@ export function registerAuditCommand(program, invokeLegacy) {
211
247
  outputDir: options.outputDir,
212
248
  dryRun: Boolean(baseReport.dryRun),
213
249
  refreshIngest: Boolean(options.refresh),
250
+ isolation: baseReport.isolation || "strict",
251
+ seedFromDeterministic: baseReport.seedFromDeterministic !== false,
214
252
  });
215
253
 
216
254
  const comparison = await writeAuditComparisonArtifact({
@@ -231,6 +269,8 @@ export function registerAuditCommand(program, invokeLegacy) {
231
269
  deterministicEquivalent: comparison.comparison.deterministicEquivalent,
232
270
  addedCount: comparison.comparison.addedCount,
233
271
  removedCount: comparison.comparison.removedCount,
272
+ isolation: replayResult.isolation,
273
+ seedFromDeterministic: replayResult.seedFromDeterministic !== false,
234
274
  ingestRefresh: replayResult.ingest?.refresh || null,
235
275
  };
236
276
 
@@ -717,6 +757,7 @@ export function registerAuditCommand(program, invokeLegacy) {
717
757
  .description("Compatibility mode: run legacy local readiness + policy audit")
718
758
  .option("--path <path>", "Target repository path")
719
759
  .option("--output-dir <path>", "Artifact root for report output")
760
+ .option("--reuse-omargate <runId>", "Reuse deterministic findings from an OmarGate run id or latest")
720
761
  .option("--json", "Emit machine-readable output")
721
762
  .action(async (options, command) => {
722
763
  const legacyArgs = buildLegacyArgs(["/audit"], {
@@ -20,12 +20,24 @@ function appendOutputDirFlag(args, maybeOutputDir) {
20
20
  }
21
21
 
22
22
  function appendPassthroughFlag(args, flagName, maybeValue) {
23
- const value = String(maybeValue || "").trim();
23
+ const value = maybeValue === undefined || maybeValue === null ? "" : String(maybeValue).trim();
24
24
  if (value) {
25
25
  args.push(flagName, value);
26
26
  }
27
27
  }
28
28
 
29
+ function appendBooleanFlag(args, flagName, maybeValue) {
30
+ if (Boolean(maybeValue)) {
31
+ args.push(flagName);
32
+ }
33
+ }
34
+
35
+ function appendNegatedBooleanFlag(args, flagName, maybeValue) {
36
+ if (maybeValue === false) {
37
+ args.push(flagName);
38
+ }
39
+ }
40
+
29
41
  export function buildLegacyArgs(baseArgs, { commandOptions = {}, command } = {}) {
30
42
  const args = [...baseArgs];
31
43
  appendPathFlag(args, commandOptions.path);
@@ -33,6 +45,25 @@ export function buildLegacyArgs(baseArgs, { commandOptions = {}, command } = {})
33
45
  if (wantsJsonOutput(commandOptions, command)) {
34
46
  args.push("--json");
35
47
  }
48
+ appendNegatedBooleanFlag(args, "--no-ai", commandOptions.ai);
49
+ appendBooleanFlag(args, "--ai-dry-run", commandOptions.aiDryRun);
50
+ appendBooleanFlag(args, "--stream", commandOptions.stream);
51
+ appendBooleanFlag(args, "--dry-run", commandOptions.dryRun);
52
+ appendNegatedBooleanFlag(args, "--no-email", commandOptions.email);
53
+ appendNegatedBooleanFlag(args, "--no-dashboard", commandOptions.dashboard);
54
+ appendPassthroughFlag(args, "--scan-mode", commandOptions.scanMode);
55
+ appendPassthroughFlag(args, "--max-parallel", commandOptions.maxParallel);
56
+ appendPassthroughFlag(args, "--max-cost", commandOptions.maxCost);
57
+ appendPassthroughFlag(args, "--max-runtime-minutes", commandOptions.maxRuntimeMinutes);
58
+ appendPassthroughFlag(args, "--model", commandOptions.model);
59
+ appendPassthroughFlag(args, "--provider", commandOptions.provider);
60
+ appendPassthroughFlag(args, "--reuse-omargate", commandOptions.reuseOmargate);
61
+ appendPassthroughFlag(args, "--notify-email", commandOptions.notifyEmail);
62
+ appendPassthroughFlag(args, "--email-on-complete", commandOptions.emailOnComplete);
63
+ appendPassthroughFlag(args, "--notify-session", commandOptions.notifySession);
64
+ appendPassthroughFlag(args, "--devtestbot-base-url", commandOptions.devtestbotBaseUrl);
65
+ appendPassthroughFlag(args, "--devtestbot-scope", commandOptions.devtestbotScope);
66
+ appendNegatedBooleanFlag(args, "--no-devtestbot", commandOptions.devtestbot);
36
67
  // Omar Gate per-persona filter flags (A-CLI-1).
37
68
  appendPassthroughFlag(args, "--persona", commandOptions.persona);
38
69
  appendPassthroughFlag(args, "--skip-persona", commandOptions.skipPersona);
@@ -48,9 +48,13 @@ export function registerOmarGateCommand(program, invokeLegacy) {
48
48
  .option("--skip-persona <csv>", "Skip these personas (comma-separated IDs)")
49
49
  .option("--stream", "Emit NDJSON events to stdout as personas work file-by-file")
50
50
  .option("--notify-email <addr>", "Send final report to this email (default: account email)")
51
+ .option("--email-on-complete <addr>", "Trigger the API-side DD report email after the run completes")
51
52
  .option("--notify-session <session-id>", "Stream progress into this Senti session (default: auto-start)")
52
53
  .option("--no-email", "Skip email dispatch")
53
54
  .option("--no-dashboard", "Skip dashboard card persistence")
55
+ .option("--devtestbot-base-url <url>", "Approved absolute URL for devTestBot browser lanes")
56
+ .option("--devtestbot-scope <scope>", "devTestBot runtime scope (default: orchestrator decides)")
57
+ .option("--no-devtestbot", "Skip the automated devTestBot phase")
54
58
  .option("--dry-run", "Validate config + emit plan.json; skip LLM calls")
55
59
  .option("--json", "Emit machine-readable final output")
56
60
  .action(async (options, command) => {