sentinelayer-cli 0.6.2 → 0.8.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/README.md +1009 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +64 -63
- package/src/agents/ai-governance/index.js +12 -0
- package/src/agents/ai-governance/tools/base.js +171 -0
- package/src/agents/ai-governance/tools/eval-regression.js +47 -0
- package/src/agents/ai-governance/tools/hitl-audit.js +81 -0
- package/src/agents/ai-governance/tools/index.js +52 -0
- package/src/agents/ai-governance/tools/prompt-drift.js +42 -0
- package/src/agents/ai-governance/tools/provenance-check.js +69 -0
- package/src/agents/backend/index.js +12 -0
- package/src/agents/backend/tools/base.js +189 -0
- package/src/agents/backend/tools/circuit-breaker-check.js +123 -0
- package/src/agents/backend/tools/idempotency-audit.js +105 -0
- package/src/agents/backend/tools/index.js +87 -0
- package/src/agents/backend/tools/retry-audit.js +132 -0
- package/src/agents/backend/tools/timeout-audit.js +144 -0
- package/src/agents/code-quality/index.js +12 -0
- package/src/agents/code-quality/tools/base.js +159 -0
- package/src/agents/code-quality/tools/complexity-measure.js +197 -0
- package/src/agents/code-quality/tools/coupling-analysis.js +81 -0
- package/src/agents/code-quality/tools/cycle-detect.js +49 -0
- package/src/agents/code-quality/tools/dep-graph.js +196 -0
- package/src/agents/code-quality/tools/index.js +89 -0
- package/src/agents/data-layer/index.js +12 -0
- package/src/agents/data-layer/tools/base.js +181 -0
- package/src/agents/data-layer/tools/index-audit.js +165 -0
- package/src/agents/data-layer/tools/index.js +83 -0
- package/src/agents/data-layer/tools/migration-scan.js +135 -0
- package/src/agents/data-layer/tools/query-explain.js +120 -0
- package/src/agents/data-layer/tools/tenancy-scan.js +166 -0
- package/src/agents/documentation/index.js +12 -0
- package/src/agents/documentation/tools/api-diff.js +91 -0
- package/src/agents/documentation/tools/base.js +151 -0
- package/src/agents/documentation/tools/dead-link-check.js +58 -0
- package/src/agents/documentation/tools/docstring-coverage.js +78 -0
- package/src/agents/documentation/tools/index.js +52 -0
- package/src/agents/documentation/tools/readme-freshness.js +61 -0
- package/src/agents/envelope/fix-cycle.js +45 -0
- package/src/agents/envelope/index.js +31 -0
- package/src/agents/envelope/loop.js +150 -0
- package/src/agents/envelope/pulse.js +18 -0
- package/src/agents/envelope/stream.js +40 -0
- package/src/agents/infrastructure/index.js +12 -0
- package/src/agents/infrastructure/tools/base.js +171 -0
- package/src/agents/infrastructure/tools/checkov-run.js +32 -0
- package/src/agents/infrastructure/tools/drift-detect.js +59 -0
- package/src/agents/infrastructure/tools/iam-least-priv-check.js +78 -0
- package/src/agents/infrastructure/tools/index.js +52 -0
- package/src/agents/infrastructure/tools/tflint-run.js +31 -0
- package/src/agents/jules/config/definition.js +160 -160
- package/src/agents/jules/config/system-prompt.js +182 -182
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -17
- package/src/agents/jules/loop.js +460 -450
- package/src/agents/jules/pulse.js +10 -10
- package/src/agents/jules/stream.js +187 -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 +315 -309
- package/src/agents/jules/tools/aidenid-email.js +189 -189
- package/src/agents/jules/tools/auth-audit.js +1708 -1691
- package/src/agents/jules/tools/dispatch.js +340 -335
- package/src/agents/jules/tools/file-edit.js +2 -2
- package/src/agents/jules/tools/file-read.js +2 -2
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -2
- package/src/agents/jules/tools/grep.js +2 -2
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -2
- package/src/agents/jules/tools/runtime-audit.js +507 -507
- package/src/agents/jules/tools/shell.js +2 -2
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/mode.js +113 -0
- package/src/agents/observability/index.js +12 -0
- package/src/agents/observability/tools/alert-audit.js +39 -0
- package/src/agents/observability/tools/base.js +181 -0
- package/src/agents/observability/tools/dashboard-gap.js +42 -0
- package/src/agents/observability/tools/index.js +54 -0
- package/src/agents/observability/tools/log-schema-check.js +74 -0
- package/src/agents/observability/tools/span-coverage.js +74 -0
- package/src/agents/persona-visuals.js +102 -61
- package/src/agents/release/index.js +12 -0
- package/src/agents/release/tools/base.js +181 -0
- package/src/agents/release/tools/changelog-diff.js +86 -0
- package/src/agents/release/tools/feature-flag-audit.js +126 -0
- package/src/agents/release/tools/index.js +61 -0
- package/src/agents/release/tools/rollback-verify.js +129 -0
- package/src/agents/release/tools/semver-check.js +109 -0
- package/src/agents/reliability/index.js +12 -0
- package/src/agents/reliability/tools/backpressure-check.js +129 -0
- package/src/agents/reliability/tools/base.js +181 -0
- package/src/agents/reliability/tools/chaos-probe.js +109 -0
- package/src/agents/reliability/tools/graceful-degradation-check.js +114 -0
- package/src/agents/reliability/tools/health-check-audit.js +111 -0
- package/src/agents/reliability/tools/index.js +87 -0
- package/src/agents/run-persona.js +109 -0
- package/src/agents/security/index.js +12 -0
- package/src/agents/security/tools/authz-audit.js +134 -0
- package/src/agents/security/tools/base.js +190 -0
- package/src/agents/security/tools/crypto-review.js +175 -0
- package/src/agents/security/tools/index.js +97 -0
- package/src/agents/security/tools/sast-scan.js +175 -0
- package/src/agents/security/tools/secrets-scan.js +216 -0
- package/src/agents/shared-tools/dispatch-core.js +320 -315
- package/src/agents/shared-tools/file-edit.js +180 -180
- package/src/agents/shared-tools/file-read.js +100 -100
- package/src/agents/shared-tools/glob.js +168 -168
- package/src/agents/shared-tools/grep.js +228 -228
- package/src/agents/shared-tools/index.js +46 -46
- package/src/agents/shared-tools/path-guards.js +161 -161
- package/src/agents/shared-tools/shell.js +383 -383
- package/src/agents/supply-chain/index.js +12 -0
- package/src/agents/supply-chain/tools/attestation-check.js +42 -0
- package/src/agents/supply-chain/tools/base.js +151 -0
- package/src/agents/supply-chain/tools/index.js +52 -0
- package/src/agents/supply-chain/tools/lockfile-integrity.js +73 -0
- package/src/agents/supply-chain/tools/package-verify.js +56 -0
- package/src/agents/supply-chain/tools/sbom-diff.js +34 -0
- package/src/agents/testing/index.js +12 -0
- package/src/agents/testing/tools/base.js +202 -0
- package/src/agents/testing/tools/coverage-gap.js +144 -0
- package/src/agents/testing/tools/flake-detect.js +125 -0
- package/src/agents/testing/tools/index.js +85 -0
- package/src/agents/testing/tools/mutation-test.js +143 -0
- package/src/agents/testing/tools/snapshot-diff.js +103 -0
- package/src/ai/aidenid.js +1021 -1009
- package/src/ai/client.js +553 -553
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -137
- 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 +428 -371
- package/src/auth/http.js +681 -611
- package/src/auth/service.js +1106 -1106
- package/src/auth/session-store.js +813 -813
- package/src/cli.js +257 -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 +1171 -1166
- package/src/commands/auth.js +419 -419
- package/src/commands/chat.js +184 -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 +20 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +63 -29
- package/src/commands/persona.js +65 -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 +865 -872
- package/src/commands/session.js +1238 -0
- package/src/commands/spec.js +771 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -511
- 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/coord/events-log.js +141 -0
- package/src/coord/handshake.js +719 -0
- package/src/coord/index.js +35 -0
- package/src/coord/paths.js +84 -0
- package/src/coord/priority.js +62 -0
- package/src/coord/tarjan.js +157 -0
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tokenizer.js +160 -0
- package/src/cost/tracker.js +232 -171
- package/src/daemon/artifact-lineage.js +896 -534
- package/src/daemon/assignment-ledger.js +1083 -770
- package/src/daemon/ast-drift.js +496 -0
- 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 +1209 -626
- package/src/daemon/fix-cycle.js +384 -377
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/ingest-refresh.js +79 -11
- package/src/daemon/jira-lifecycle.js +767 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -327
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/scope-engine.js +1068 -0
- package/src/daemon/watchdog.js +971 -971
- package/src/events/schema.js +190 -0
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +933 -918
- package/src/ingest/ownership.js +380 -0
- package/src/interactive/index.js +97 -97
- package/src/legacy-cli.js +3228 -2994
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/orchestrator/kai-chen.js +126 -0
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +136 -118
- package/src/review/ai-review.js +672 -679
- package/src/review/compliance-pack.js +389 -0
- package/src/review/investor-dd-config.js +54 -0
- package/src/review/investor-dd-file-loop.js +303 -0
- package/src/review/investor-dd-file-router.js +406 -0
- package/src/review/investor-dd-html-report.js +233 -0
- package/src/review/investor-dd-notification.js +120 -0
- package/src/review/investor-dd-orchestrator.js +405 -0
- package/src/review/investor-dd-persona-runner.js +275 -0
- package/src/review/live-validator.js +253 -0
- package/src/review/local-review.js +1351 -1305
- package/src/review/omargate-interactive.js +68 -68
- package/src/review/omargate-orchestrator.js +492 -300
- package/src/review/persona-prompts.js +484 -296
- package/src/review/reconciliation-rules.js +329 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/reproducibility-chain.js +136 -0
- package/src/review/scan-modes.js +147 -42
- 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/session/agent-registry.js +359 -0
- package/src/session/analytics.js +479 -0
- package/src/session/daemon.js +1396 -0
- package/src/session/file-locks.js +666 -0
- package/src/session/paths.js +37 -0
- package/src/session/recap.js +567 -0
- package/src/session/redact.js +82 -0
- package/src/session/runtime-bridge.js +762 -0
- package/src/session/scoring.js +406 -0
- package/src/session/setup-guides.js +304 -0
- package/src/session/store.js +704 -0
- package/src/session/stream.js +333 -0
- package/src/session/sync.js +753 -0
- package/src/session/tasks.js +1054 -0
- package/src/session/templates.js +188 -0
- package/src/spec/generator.js +619 -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 +569 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -234
- package/src/telemetry/sync.js +203 -203
- package/src/ui/command-hints.js +13 -13
- package/src/ui/markdown.js +220 -220
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live-web validator for investor-DD (#investor-dd-25..28).
|
|
3
|
+
*
|
|
4
|
+
* Jules owns this lane. For each interactive element discovered by
|
|
5
|
+
* scanning the frontend source (buttons, forms, links), the validator
|
|
6
|
+
* provisions an ephemeral AIdenID identity, drives devTestBot to
|
|
7
|
+
* perform the interaction against the running site, and captures:
|
|
8
|
+
*
|
|
9
|
+
* - the observed HTTP status
|
|
10
|
+
* - console errors
|
|
11
|
+
* - network errors
|
|
12
|
+
* - navigation outcome
|
|
13
|
+
* - a short free-form observed-behavior summary
|
|
14
|
+
* - trace + video URIs (supplied by devTestBot)
|
|
15
|
+
*
|
|
16
|
+
* The module is driven through a pluggable client surface so the main
|
|
17
|
+
* flow can be unit-tested without spinning up a real browser and a
|
|
18
|
+
* real AIdenID tenant. Production wiring is a separate PR that swaps
|
|
19
|
+
* the stub client for the real devTestBot + AIdenID SDKs.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import fsp from "node:fs/promises";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
|
|
25
|
+
const INTERACTIVE_TAGS = Object.freeze([
|
|
26
|
+
"button",
|
|
27
|
+
"a",
|
|
28
|
+
"input",
|
|
29
|
+
"form",
|
|
30
|
+
"select",
|
|
31
|
+
"textarea",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const SOURCE_EXTENSIONS = Object.freeze([".tsx", ".jsx", ".html", ".vue", ".svelte"]);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Walk the frontend directory and extract candidate interactive
|
|
38
|
+
* elements from JSX/HTML-like files. Deliberately simple regex-based
|
|
39
|
+
* extraction; misses dynamic elements. Caller can fall back to a live
|
|
40
|
+
* DOM crawl when static extraction returns < 80% of expected element
|
|
41
|
+
* counts.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} rootPath
|
|
44
|
+
* @param {string[]} [globLike] Optional include roots (default common frontend folders).
|
|
45
|
+
* @returns {Promise<Array<{elementLabel: string, sourceFile: string, lineIndex: number}>>}
|
|
46
|
+
*/
|
|
47
|
+
export async function discoverInteractiveElements(rootPath, globLike = null) {
|
|
48
|
+
const candidateRoots = globLike || [
|
|
49
|
+
"src",
|
|
50
|
+
"app",
|
|
51
|
+
"pages",
|
|
52
|
+
"components",
|
|
53
|
+
"web",
|
|
54
|
+
"frontend",
|
|
55
|
+
"client",
|
|
56
|
+
];
|
|
57
|
+
const elements = [];
|
|
58
|
+
for (const candidate of candidateRoots) {
|
|
59
|
+
const abs = path.join(rootPath, candidate);
|
|
60
|
+
try {
|
|
61
|
+
await fsp.access(abs);
|
|
62
|
+
} catch {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
await walk(abs, candidate);
|
|
66
|
+
}
|
|
67
|
+
return elements;
|
|
68
|
+
|
|
69
|
+
async function walk(abs, rel) {
|
|
70
|
+
let entries;
|
|
71
|
+
try {
|
|
72
|
+
entries = await fsp.readdir(abs, { withFileTypes: true });
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (entry.name.startsWith(".")) continue;
|
|
78
|
+
const absPath = path.join(abs, entry.name);
|
|
79
|
+
const relPath = `${rel}/${entry.name}`;
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
82
|
+
await walk(absPath, relPath);
|
|
83
|
+
} else if (entry.isFile()) {
|
|
84
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
85
|
+
if (!SOURCE_EXTENSIONS.includes(ext)) continue;
|
|
86
|
+
try {
|
|
87
|
+
const stat = await fsp.stat(absPath);
|
|
88
|
+
if (stat.size > 512 * 1024) continue;
|
|
89
|
+
const text = await fsp.readFile(absPath, "utf-8");
|
|
90
|
+
extractFromText(text, relPath, elements);
|
|
91
|
+
} catch {
|
|
92
|
+
// skip unreadable
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function extractFromText(text, sourceFile, elements) {
|
|
100
|
+
const lines = text.split(/\r?\n/);
|
|
101
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
102
|
+
const line = lines[i];
|
|
103
|
+
for (const tag of INTERACTIVE_TAGS) {
|
|
104
|
+
// Match both lowercase and Capitalized component forms; avoid
|
|
105
|
+
// overmatching by requiring a tag-like opener.
|
|
106
|
+
const re = new RegExp(`<${tag}[\\s>]|<${tag[0].toUpperCase()}${tag.slice(1)}[\\s>]`, "i");
|
|
107
|
+
if (!re.test(line)) continue;
|
|
108
|
+
const labelMatch =
|
|
109
|
+
/(?:aria-label|title|data-testid|id)="([^"]+)"/i.exec(line) ||
|
|
110
|
+
/>([^<]{1,40})</.exec(line);
|
|
111
|
+
const elementLabel = labelMatch ? labelMatch[1].trim() : `${tag}-anon-${i}`;
|
|
112
|
+
elements.push({
|
|
113
|
+
elementLabel,
|
|
114
|
+
sourceFile,
|
|
115
|
+
lineIndex: i + 1,
|
|
116
|
+
});
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @typedef {object} DevTestBotClient
|
|
124
|
+
* @property {(element: {elementLabel: string, sourceFile: string}, identity: object) => Promise<LiveObservation>} interact
|
|
125
|
+
* @property {(runId: string) => Promise<{videoUri: string, traceUri: string}>} [artifact]
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @typedef {object} AidenidClient
|
|
130
|
+
* @property {(runId: string) => Promise<{identityId: string, email: string}>} provisionEphemeralIdentity
|
|
131
|
+
* @property {(identityId: string) => Promise<void>} [release]
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Run the live validator across the discovered element plan.
|
|
136
|
+
*
|
|
137
|
+
* @param {object} params
|
|
138
|
+
* @param {string} params.runId
|
|
139
|
+
* @param {Array<{elementLabel: string, sourceFile: string}>} params.elements
|
|
140
|
+
* @param {DevTestBotClient} params.devTestBot
|
|
141
|
+
* @param {AidenidClient} params.aidenid
|
|
142
|
+
* @param {Function} [params.onEvent]
|
|
143
|
+
* @param {number} [params.maxInteractions] - Cap; defaults to elements.length.
|
|
144
|
+
* @returns {Promise<{identity: object, observations: Array<object>, skipped: number}>}
|
|
145
|
+
*/
|
|
146
|
+
export async function runLiveValidator({
|
|
147
|
+
runId,
|
|
148
|
+
elements,
|
|
149
|
+
devTestBot,
|
|
150
|
+
aidenid,
|
|
151
|
+
onEvent = () => {},
|
|
152
|
+
maxInteractions = Infinity,
|
|
153
|
+
} = {}) {
|
|
154
|
+
if (!runId) throw new TypeError("runLiveValidator requires runId");
|
|
155
|
+
if (!Array.isArray(elements)) throw new TypeError("runLiveValidator requires elements array");
|
|
156
|
+
if (!devTestBot || typeof devTestBot.interact !== "function") {
|
|
157
|
+
throw new TypeError("runLiveValidator requires a devTestBot client with interact()");
|
|
158
|
+
}
|
|
159
|
+
if (!aidenid || typeof aidenid.provisionEphemeralIdentity !== "function") {
|
|
160
|
+
throw new TypeError(
|
|
161
|
+
"runLiveValidator requires an AIdenID client with provisionEphemeralIdentity()",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
onEvent({ type: "live_validator_start", runId, elementCount: elements.length });
|
|
166
|
+
const identity = await aidenid.provisionEphemeralIdentity(runId);
|
|
167
|
+
onEvent({ type: "live_validator_identity_ready", runId, identityId: identity.identityId });
|
|
168
|
+
|
|
169
|
+
const observations = [];
|
|
170
|
+
let skipped = 0;
|
|
171
|
+
const budget = Number.isFinite(maxInteractions) ? maxInteractions : elements.length;
|
|
172
|
+
for (let i = 0; i < Math.min(elements.length, budget); i += 1) {
|
|
173
|
+
const element = elements[i];
|
|
174
|
+
onEvent({ type: "live_validator_interaction_start", runId, element });
|
|
175
|
+
try {
|
|
176
|
+
const obs = await devTestBot.interact(element, identity);
|
|
177
|
+
const enriched = {
|
|
178
|
+
...obs,
|
|
179
|
+
sourceFile: element.sourceFile,
|
|
180
|
+
elementLabel: element.elementLabel,
|
|
181
|
+
interactionId: obs.interactionId || `${element.sourceFile}#${i}`,
|
|
182
|
+
};
|
|
183
|
+
observations.push(enriched);
|
|
184
|
+
onEvent({
|
|
185
|
+
type: "live_validator_interaction_complete",
|
|
186
|
+
runId,
|
|
187
|
+
interactionId: enriched.interactionId,
|
|
188
|
+
});
|
|
189
|
+
} catch (err) {
|
|
190
|
+
skipped += 1;
|
|
191
|
+
onEvent({
|
|
192
|
+
type: "live_validator_interaction_error",
|
|
193
|
+
runId,
|
|
194
|
+
element,
|
|
195
|
+
error: err instanceof Error ? err.message : String(err),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (typeof aidenid.release === "function") {
|
|
201
|
+
try {
|
|
202
|
+
await aidenid.release(identity.identityId);
|
|
203
|
+
} catch {
|
|
204
|
+
// release errors never block the report
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
onEvent({
|
|
209
|
+
type: "live_validator_complete",
|
|
210
|
+
runId,
|
|
211
|
+
observationCount: observations.length,
|
|
212
|
+
skipped,
|
|
213
|
+
});
|
|
214
|
+
return { identity, observations, skipped };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Build a lookup map keyed by `sourceFile:lineIndex` so the
|
|
219
|
+
* reconciliation engine can pair each source finding with 0 or 1
|
|
220
|
+
* matching live observation.
|
|
221
|
+
*
|
|
222
|
+
* @param {Array<{sourceFile: string, lineIndex?: number, interactionId: string}>} observations
|
|
223
|
+
* @returns {Map<string, object>}
|
|
224
|
+
*/
|
|
225
|
+
export function buildObservationIndex(observations) {
|
|
226
|
+
const map = new Map();
|
|
227
|
+
for (const obs of observations || []) {
|
|
228
|
+
if (!obs.sourceFile) continue;
|
|
229
|
+
const fileKey = obs.sourceFile;
|
|
230
|
+
if (obs.lineIndex) {
|
|
231
|
+
map.set(`${fileKey}:${obs.lineIndex}`, obs);
|
|
232
|
+
}
|
|
233
|
+
if (!map.has(fileKey)) {
|
|
234
|
+
map.set(fileKey, obs);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return map;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Pair function factory for reconcileFindings(). Looks up an
|
|
242
|
+
* observation for each finding by (file, line) or (file) fallback.
|
|
243
|
+
*
|
|
244
|
+
* @param {Map<string, object>} index
|
|
245
|
+
* @returns {(finding: object) => object | null}
|
|
246
|
+
*/
|
|
247
|
+
export function createFindingObservationPair(index) {
|
|
248
|
+
return (finding) => {
|
|
249
|
+
if (!finding || !finding.file) return null;
|
|
250
|
+
const key = finding.line ? `${finding.file}:${finding.line}` : finding.file;
|
|
251
|
+
return index.get(key) || index.get(finding.file) || null;
|
|
252
|
+
};
|
|
253
|
+
}
|