nexus-agents 8.23.2 → 8.23.3

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 (28) hide show
  1. package/dist/{chunk-3CHPU46R.js → chunk-6YP5DJUF.js} +2 -2
  2. package/dist/{chunk-KIAMP743.js → chunk-7JD5344Q.js} +12 -5
  3. package/dist/chunk-7JD5344Q.js.map +1 -0
  4. package/dist/{chunk-TBOM7PJ7.js → chunk-AZOIWYKL.js} +48 -15
  5. package/dist/chunk-AZOIWYKL.js.map +1 -0
  6. package/dist/{chunk-OXUJBY4W.js → chunk-DP2H62OR.js} +2 -2
  7. package/dist/{chunk-ZFZJ6LTJ.js → chunk-FAI5Z3TK.js} +2 -2
  8. package/dist/{chunk-TCUEFOPW.js → chunk-JA2ZRSRP.js} +3 -3
  9. package/dist/{chunk-M7TUO6O7.js → chunk-MEZUIN3R.js} +3 -3
  10. package/dist/cli.js +9 -9
  11. package/dist/{consensus-vote-NQPSFVEF.js → consensus-vote-4SBBG2AZ.js} +4 -4
  12. package/dist/{improvement-review-5HMJ2NCH.js → improvement-review-YCNNHLNL.js} +4 -4
  13. package/dist/index.d.ts +16 -2
  14. package/dist/index.js +6 -6
  15. package/dist/{pr-reviewer-helpers-3VQNHIXI.js → pr-reviewer-helpers-HT3LU55R.js} +2 -2
  16. package/dist/{setup-data-dir-IJ6CYVCL.js → setup-data-dir-OQ3GDPRO.js} +3 -3
  17. package/package.json +1 -1
  18. package/dist/chunk-KIAMP743.js.map +0 -1
  19. package/dist/chunk-TBOM7PJ7.js.map +0 -1
  20. /package/dist/{chunk-3CHPU46R.js.map → chunk-6YP5DJUF.js.map} +0 -0
  21. /package/dist/{chunk-OXUJBY4W.js.map → chunk-DP2H62OR.js.map} +0 -0
  22. /package/dist/{chunk-ZFZJ6LTJ.js.map → chunk-FAI5Z3TK.js.map} +0 -0
  23. /package/dist/{chunk-TCUEFOPW.js.map → chunk-JA2ZRSRP.js.map} +0 -0
  24. /package/dist/{chunk-M7TUO6O7.js.map → chunk-MEZUIN3R.js.map} +0 -0
  25. /package/dist/{consensus-vote-NQPSFVEF.js.map → consensus-vote-4SBBG2AZ.js.map} +0 -0
  26. /package/dist/{improvement-review-5HMJ2NCH.js.map → improvement-review-YCNNHLNL.js.map} +0 -0
  27. /package/dist/{pr-reviewer-helpers-3VQNHIXI.js.map → pr-reviewer-helpers-HT3LU55R.js.map} +0 -0
  28. /package/dist/{setup-data-dir-IJ6CYVCL.js.map → setup-data-dir-OQ3GDPRO.js.map} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  toolStructuredError,
8
8
  toolSuccessStructured,
9
9
  wrapToolWithTimeout
10
- } from "./chunk-ZFZJ6LTJ.js";
10
+ } from "./chunk-FAI5Z3TK.js";
11
11
  import {
12
12
  JsonlStore,
13
13
  SECURITY_KEYWORDS,
@@ -2159,4 +2159,4 @@ export {
2159
2159
  runImprovementReview,
2160
2160
  registerImprovementReviewTool
2161
2161
  };
2162
- //# sourceMappingURL=chunk-3CHPU46R.js.map
2162
+ //# sourceMappingURL=chunk-6YP5DJUF.js.map
@@ -54,6 +54,7 @@ function formatReviewStats(result) {
54
54
 
55
55
  // src/dogfooding/pr-reviewer-helpers.ts
56
56
  var repLogger = createLogger({ component: "PRReviewer.reputation" });
57
+ var PARTIAL_COVERAGE_NOTE = "Security expert findings coverage is partial";
57
58
  async function fetchAccountAgeDays(provider, username) {
58
59
  try {
59
60
  const result = await provider.fetchUserMetadata(username);
@@ -128,10 +129,9 @@ function parseCategory(value) {
128
129
  return "code_quality";
129
130
  }
130
131
  function extractSummary(output) {
131
- if (typeof output.summary === "string") return output.summary;
132
- if (typeof output.content === "string") return output.content;
133
- if (typeof output.message === "string") return output.message;
134
- return "Review completed";
132
+ const summary = extractStringField(output, "summary", "content", "message") ?? "Review completed";
133
+ return output.findingsCoverage === "partial" ? `${summary}
134
+ ${PARTIAL_COVERAGE_NOTE}` : summary;
135
135
  }
136
136
  function extractStringField(record, ...keys) {
137
137
  for (const key of keys) {
@@ -234,6 +234,13 @@ function parseVerdictMarker(value) {
234
234
  function parseExpertReview(output, expertId, minSeverity) {
235
235
  const findings = parseFindings(output, expertId, minSeverity);
236
236
  const verdict = parseVerdictMarker(output.verdict) ?? parseVerdictMarker(output.content);
237
+ if (output.findingsCoverage === "unmeasured") {
238
+ return { findings, verdict: "errored" };
239
+ }
240
+ if (output.findingsCoverage === "partial") {
241
+ const partialVerdict = verdict === "changes_requested" ? verdict : "findings";
242
+ return { findings, verdict: partialVerdict };
243
+ }
237
244
  if (verdict !== void 0) return { findings, verdict };
238
245
  const hasFindingShape = collectSources(output).some((source) => Array.isArray(source));
239
246
  const hasWarningShape = parseWarnings(output.warnings).length > 0;
@@ -383,4 +390,4 @@ export {
383
390
  createFailedReview,
384
391
  reviewPostingBlock
385
392
  };
386
- //# sourceMappingURL=chunk-KIAMP743.js.map
393
+ //# sourceMappingURL=chunk-7JD5344Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dogfooding/pr-reviewer-helpers.ts","../src/dogfooding/pr-review-stats.ts"],"sourcesContent":["/**\n * nexus-agents/dogfooding - PR Reviewer Helpers\n *\n * Helper functions for PR review formatting and aggregation.\n *\n * @module dogfooding/pr-reviewer-helpers\n * (Source: Issue #161, Alignment Roadmap Phase 3)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Result } from '../core/index.js';\nimport { getTimeProvider, createLogger, ok, err } from '../core/index.js';\nimport type { ScmUserMetadata } from '../scm/types.js';\nimport type {\n PRMetadata,\n PRReviewDraft,\n PRTrustAssessment,\n ExpertReviewResult,\n ReviewFinding,\n ReviewCategory,\n ReviewSeverity,\n ReviewDecision,\n} from './pr-review-types.js';\nimport {\n SEVERITY_ORDER,\n CATEGORY_DISPLAY_NAMES,\n SEVERITY_EMOJI,\n DECISION_EMOJI,\n} from './pr-review-types.js';\nimport { sanitizeInput } from '../security/input-sanitizer.js';\nimport { allOf } from '../utils/verdict-aggregation.js';\nimport { assessReputation } from '../security/reputation-model.js';\nimport type {\n ReputationCache,\n ReputationGateDecision,\n ReputationAssessment,\n GitHubUserMetadata,\n} from '../security/reputation-model.js';\nimport type { FirewallResult } from '../security/firewall/firewall-pipeline.js';\nimport { formatReviewStats } from './pr-review-stats.js';\n\nconst repLogger = createLogger({ component: 'PRReviewer.reputation' });\nconst PARTIAL_COVERAGE_NOTE = 'Security expert findings coverage is partial';\n\n// =============================================================================\n// Reputation Gating Helpers (#3123, epic #3118 Phase 5)\n// =============================================================================\n\n/**\n * Best-effort account-age lookup for a PR author (#3133, Phase-3 equivalent for\n * the PR path). Returns the author's real account age in days via the provider's\n * `fetchUserMetadata`, or `undefined` on any failure (err Result, unparseable\n * `createdAt`, or an unexpected rejection) — never fabricated, never throws, so\n * the review is never blocked by this lookup.\n */\nexport async function fetchAccountAgeDays(\n provider: { fetchUserMetadata: (u: string) => Promise<Result<ScmUserMetadata, Error>> },\n username: string\n): Promise<number | undefined> {\n try {\n const result = await provider.fetchUserMetadata(username);\n if (!result.ok) return undefined;\n const createdMs = Date.parse(result.value.createdAt);\n if (!Number.isFinite(createdMs)) return undefined;\n return Math.floor((getTimeProvider().now() - createdMs) / 86_400_000);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Assesses the PR author's reputation from the signals available in the PR\n * event: author association + injection flags from the (sanitized) PR body, plus\n * the author's real account age when it was fetched (#3133). `accountAgeDays` is\n * OMITTED when the lookup failed — never fabricated, so the engine skips the\n * `new_account` signal. Returns undefined when reputation is disabled.\n */\nexport function assessPRReputation(\n pr: PRMetadata,\n cache: ReputationCache,\n enableReputation: boolean,\n accountAgeDays: number | undefined\n): ReputationAssessment | undefined {\n if (!enableReputation) return undefined;\n // Only `injectionFlags` is consumed here, and injection detection is\n // role-independent — the userRole arg ('unknown') and the sanitizer's own\n // trustTier output are intentionally irrelevant to this call.\n const sanitizeResult = sanitizeInput(pr.body, 'unknown', pr.author);\n const metadata: GitHubUserMetadata = {\n username: pr.author,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n authorAssociation: pr.authorAssociation,\n injectionFlags: sanitizeResult.injectionFlags,\n };\n return assessReputation(metadata, cache);\n}\n\n/** Builds the observability assessment surfaced on the review result (#3123). */\nexport function buildPRTrustAssessment(\n firewall: Pick<FirewallResult, 'trust' | 'isAllowlisted' | 'auditSink'>,\n reputation: ReputationAssessment | undefined,\n gateDecision: ReputationGateDecision\n): PRTrustAssessment {\n const trustResult = firewall.trust;\n // Tier-1 (owner/allowlisted) authors cannot be suspicious.\n const isTier1 = trustResult.trustTier === '1';\n return {\n trustTier: trustResult.trustTier,\n userRole: trustResult.userRole,\n // Measured or absent (#4992): never the classifier's default `false`.\n ...(firewall.isAllowlisted !== undefined ? { isAllowlisted: firewall.isAllowlisted } : {}),\n auditSink: firewall.auditSink,\n reputationScore: reputation?.reputationScore,\n suspiciousSignals: isTier1 ? [] : (reputation?.suspiciousSignals ?? []),\n isSuspicious: isTier1 ? false : (reputation?.isSuspicious ?? false),\n enforcedTrustTier: gateDecision.enforcedTier,\n reputationReconciledTier: gateDecision.reconciledTier,\n gatingMode: gateDecision.mode,\n };\n}\n\n/**\n * Reads the reputation gate decision the firewall computed for this PR (#3123\n * gating; #4992 review: ONE gate, shared with the firewall's Rule-of-Two\n * signal) and builds the assessment surfaced on the result. A suppressed\n * demotion (audit/off) is logged. Fails closed when the firewall returned no\n * gate — structurally unreachable, since the caller always supplies the\n * reputation option, but the classifier tier must never stand in for it.\n */\nexport function gatePRAuthor(\n pr: PRMetadata,\n firewall: Pick<FirewallResult, 'trust' | 'isAllowlisted' | 'auditSink' | 'reputationGate'>,\n reputation: ReputationAssessment | undefined\n): Result<{ gateDecision: ReputationGateDecision; trustAssessment: PRTrustAssessment }, Error> {\n const trustResult = firewall.trust;\n const gateDecision = firewall.reputationGate;\n if (gateDecision === undefined) {\n return err(new Error('Untrusted-input firewall returned no reputation gate decision'));\n }\n if (gateDecision.demotionSuppressed) {\n repLogger.warn('Reputation demotion suppressed by gating mode (would block under enforce)', {\n prNumber: pr.number,\n author: pr.author,\n mode: gateDecision.mode,\n classifierTier: trustResult.trustTier,\n reconciledTier: gateDecision.reconciledTier,\n });\n }\n return ok({\n gateDecision,\n trustAssessment: buildPRTrustAssessment(firewall, reputation, gateDecision),\n });\n}\n\n// =============================================================================\n// Parsing Helpers\n// =============================================================================\n\nexport function parseSeverity(value: unknown): ReviewSeverity {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in SEVERITY_ORDER) return lower as ReviewSeverity;\n }\n return 'medium';\n}\n\nexport function parseCategory(value: unknown): ReviewCategory {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in CATEGORY_DISPLAY_NAMES) return lower as ReviewCategory;\n }\n return 'code_quality';\n}\n\nexport function extractSummary(output: Record<string, unknown>): string {\n const summary = extractStringField(output, 'summary', 'content', 'message') ?? 'Review completed';\n return output.findingsCoverage === 'partial' ? `${summary}\\n${PARTIAL_COVERAGE_NOTE}` : summary;\n}\n\nexport function extractStringField(\n record: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === 'string') return value;\n }\n return undefined;\n}\n\n/** Formats fetched patches and reports how many were supplied without truncation. */\nexport function formatDiffs(pr: PRMetadata): {\n readonly text: string;\n readonly filesIncluded: number;\n} {\n const maxDiffLength = 2000;\n let totalLength = 0;\n let filesIncluded = 0;\n const diffs: string[] = [];\n for (const file of pr.files) {\n if (file.patch === undefined) continue;\n const diff = `\\`\\`\\`diff\\n# ${file.filename}\\n${file.patch}\\n\\`\\`\\``;\n if (totalLength + diff.length > maxDiffLength * pr.files.length) {\n diffs.push(`# ${file.filename}\\n(diff truncated)`);\n } else {\n diffs.push(diff);\n totalLength += diff.length;\n filesIncluded++;\n }\n }\n return { text: diffs.join('\\n\\n'), filesIncluded };\n}\n\n// =============================================================================\n// Finding Parsing\n// =============================================================================\n\nexport function parseFindings(\n output: Record<string, unknown>,\n expertId: string,\n minSeverity: ReviewSeverity\n): ReviewFinding[] {\n const minOrder = SEVERITY_ORDER[minSeverity];\n const sources = collectSources(output);\n\n const findings: ReviewFinding[] = [];\n for (const source of sources) {\n if (!Array.isArray(source)) continue;\n for (const item of source) {\n const finding = parseOneFinding(item, expertId, minOrder);\n if (finding !== null) findings.push(finding);\n }\n }\n for (const warning of parseWarnings(output.warnings)) {\n const finding = parseWarningFinding(warning, expertId, minOrder);\n if (finding !== null) findings.push(finding);\n }\n return findings;\n}\n\nfunction collectSources(output: Record<string, unknown>): unknown[] {\n return [\n output.findings,\n output.vulnerabilities,\n output.issues,\n (output as { content?: { findings?: unknown } }).content,\n ];\n}\n\nfunction parseOneFinding(item: unknown, expertId: string, minOrder: number): ReviewFinding | null {\n if (typeof item !== 'object' || item === null) return null;\n\n const record = item as Record<string, unknown>;\n const severity = parseSeverity(record.severity);\n if (SEVERITY_ORDER[severity] < minOrder) return null;\n\n return {\n id: randomUUID(),\n category: parseCategory(record.category),\n severity,\n title: extractStringField(record, 'title', 'name') ?? 'Finding',\n description: extractStringField(record, 'description', 'message') ?? '',\n file: typeof record.file === 'string' ? record.file : undefined,\n line: typeof record.line === 'number' ? record.line : undefined,\n suggestion: typeof record.suggestion === 'string' ? record.suggestion : undefined,\n expertId,\n confidence: typeof record.confidence === 'number' ? record.confidence : 0.7,\n };\n}\n\nfunction parseWarnings(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((warning): warning is string => typeof warning === 'string');\n}\n\nfunction parseWarningFinding(\n warning: string,\n expertId: string,\n minOrder: number\n): ReviewFinding | null {\n const severity = parseSeverity(\n warning.match(/\\b(critical|high|medium|low|info)-?severity\\b/i)?.[1]\n );\n return parseOneFinding(\n {\n title: 'Expert warning',\n description: warning,\n severity,\n category: parseCategory(expertId.replace(/-expert$/, '')),\n },\n expertId,\n minOrder\n );\n}\n\ntype ExpertVerdict = 'approved' | 'changes_requested' | 'findings' | 'errored';\n\ninterface ParsedExpertReview {\n readonly findings: ReviewFinding[];\n readonly verdict: ExpertVerdict;\n}\n\nfunction parseVerdictMarker(\n value: unknown\n): Exclude<ExpertVerdict, 'findings' | 'errored'> | undefined {\n if (typeof value !== 'string') return undefined;\n const normalized = value.toUpperCase().replaceAll('-', '_').replaceAll(' ', '_');\n if (/\\b(?:CHANGES_REQUESTED|REQUEST_CHANGES)\\b/.test(normalized)) return 'changes_requested';\n if (/\\b(?:APPROVED|APPROVE)\\b/.test(normalized)) return 'approved';\n return undefined;\n}\n\n/** Parses one expert output into a single, validated review verdict. */\nexport function parseExpertReview(\n output: Record<string, unknown>,\n expertId: string,\n minSeverity: ReviewSeverity\n): ParsedExpertReview {\n const findings = parseFindings(output, expertId, minSeverity);\n const verdict = parseVerdictMarker(output.verdict) ?? parseVerdictMarker(output.content);\n if (output.findingsCoverage === 'unmeasured') {\n return { findings, verdict: 'errored' };\n }\n if (output.findingsCoverage === 'partial') {\n const partialVerdict = verdict === 'changes_requested' ? verdict : 'findings';\n return { findings, verdict: partialVerdict };\n }\n if (verdict !== undefined) return { findings, verdict };\n const hasFindingShape = collectSources(output).some((source) => Array.isArray(source));\n const hasWarningShape = parseWarnings(output.warnings).length > 0;\n return { findings, verdict: hasFindingShape || hasWarningShape ? 'findings' : 'errored' };\n}\n\n// =============================================================================\n// Decision Helpers\n// =============================================================================\n\nexport function determineApproval(\n findings: ReviewFinding[],\n verdict: ExpertVerdict = 'findings'\n): boolean {\n if (verdict === 'errored' || verdict === 'changes_requested') return false;\n const hasBlocking = findings.some((f) => f.severity === 'critical' || f.severity === 'high');\n return !hasBlocking;\n}\n\nexport function determineDecision(\n reviews: ExpertReviewResult[],\n findings: ReviewFinding[]\n): ReviewDecision {\n // #5012: an expert that errored produced no verdict, so it can neither\n // approve nor object. `allOf(reviews, …, false)` already refuses to call\n // ZERO reviews unanimous approval (#4581); feeding it synthetic approvals\n // for failed experts defeated that guard by making the list non-empty.\n const completedReviews = reviews.filter((r) => r.errored !== true);\n const hasCritical = findings.some((f) => f.severity === 'critical');\n const hasHigh = findings.some((f) => f.severity === 'high');\n // Zero expert reviews is not unanimous approval (#4581): with `true` here the\n // `hasHigh && !allApproved` branch could never fire on an unreviewed PR, so a\n // HIGH finding silently downgraded from request_changes to comment.\n const allApproved = allOf(reviews, (r) => r.errored !== true && r.approved, false);\n\n if (hasCritical) return 'request_changes';\n if (hasHigh && !allApproved) return 'request_changes';\n if (findings.length > 0) return 'comment';\n // Nothing read the diff, so there is nothing to approve. `comment` posts the\n // failure summaries without asserting the change is fine.\n if (completedReviews.length === 0 || !allApproved) return 'comment';\n return 'approve';\n}\n\nexport function calculateConsensus(reviews: ExpertReviewResult[]): number {\n if (reviews.length === 0) return 1;\n const approvals = reviews.filter((r) => r.approved).length;\n return approvals / reviews.length;\n}\n\n// =============================================================================\n// Counting Helpers\n// =============================================================================\n\nexport function countBySeverity(findings: ReviewFinding[]): Record<ReviewSeverity, number> {\n const counts: Record<ReviewSeverity, number> = {\n critical: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n\n for (const f of findings) {\n counts[f.severity]++;\n }\n\n return counts;\n}\n\nexport function countByCategory(findings: ReviewFinding[]): Record<ReviewCategory, number> {\n const counts: Record<ReviewCategory, number> = {\n security: 0,\n performance: 0,\n code_quality: 0,\n testing: 0,\n documentation: 0,\n architecture: 0,\n };\n\n for (const f of findings) {\n counts[f.category]++;\n }\n\n return counts;\n}\n\nexport function sumFindings(counts: Record<ReviewSeverity, number>): number {\n return Object.values(counts).reduce((a, b) => a + b, 0);\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\nexport function generateSummary(\n pr: PRMetadata,\n reviews: ExpertReviewResult[],\n decision: ReviewDecision\n): string {\n const expertSummaries = reviews\n .map((r) => `- **${CATEGORY_DISPLAY_NAMES[r.expertType as ReviewCategory]}**: ${r.summary}`)\n .join('\\n');\n\n return `Reviewed PR #${String(pr.number)}: ${pr.title}\n\n**Decision:** ${decision.replaceAll('_', ' ')}\n**Experts consulted:** ${String(reviews.length)}\n\n${expertSummaries}`;\n}\n\n// =============================================================================\n// GitHub Comment Formatting\n// =============================================================================\n\n/**\n * Formats the review result as a GitHub comment.\n */\nexport function formatReviewComment(result: PRReviewDraft): string {\n const emoji = DECISION_EMOJI[result.decision];\n const decisionText = result.decision.replaceAll('_', ' ').toUpperCase();\n\n const findingsSection = formatFindingsSection(result);\n const statsSection = formatReviewStats(result);\n\n return `## ${emoji} Nexus Agents Review: ${decisionText}\n\n${result.summary}\n\n${findingsSection}\n\n${statsSection}\n\n---\n*Reviewed by [nexus-agents](https://github.com/nexus-substrate/nexus-agents) in ${String(result.totalDurationMs)}ms*`;\n}\n\nfunction formatFindingsSection(result: PRReviewDraft): string {\n const allFindings = result.expertReviews.flatMap((r) => r.findings);\n\n if (allFindings.length === 0) {\n return '_No issues found._';\n }\n\n const sorted = [...allFindings].sort(\n (a, b) => SEVERITY_ORDER[b.severity] - SEVERITY_ORDER[a.severity]\n );\n\n const lines = ['### Findings', ''];\n\n for (const f of sorted) {\n const emoji = SEVERITY_EMOJI[f.severity];\n const loc =\n f.file !== undefined\n ? ` (\\`${f.file}${f.line !== undefined ? `:${String(f.line)}` : ''}\\`)`\n : '';\n lines.push(`${emoji} **${f.title}**${loc}`);\n lines.push(`> ${f.description}`);\n if (f.suggestion !== undefined) {\n lines.push(`> 💡 ${f.suggestion}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Failed Review Factory\n// =============================================================================\n\nexport function createFailedReview(\n expertId: string,\n category: ReviewCategory,\n durationMs: number,\n error: string\n): ExpertReviewResult {\n return {\n expertId,\n expertType: category,\n // #5012: NOT `approved: true`. \"Don't block on failures\" made an expert\n // that never ran indistinguishable from one that read the diff and had no\n // objection, and `determineDecision` turned three of those into an APPROVE\n // posted to GitHub.\n approved: false,\n errored: true,\n summary: `Review failed: ${error}`,\n findings: [],\n durationMs,\n confidence: 0,\n };\n}\n\n/** The policy gate's verdict on posting a review, as the caller needs it. */\nexport interface ReviewPostingVerdict {\n readonly allowed: boolean;\n readonly hasRuleOfTwoViolation: boolean;\n readonly violations: readonly { rule: string; message?: string }[];\n}\n\n/**\n * Decide whether review posting is blocked, and how to describe it.\n *\n * Consumes the gate's own `allowed` rather than re-deriving a narrower\n * condition. The caller previously blocked on `hasRuleOfTwoViolation` alone\n * while `evaluatePolicy` had already computed `allowed`.\n *\n * For the action `auditReviewAction` builds, the two are currently EQUIVALENT:\n * tiers 1-2 yield no violations, tiers 3-4 yield INSUFFICIENT_TRUST +\n * UNTRUSTED_INFLUENCE + RULE_OF_TWO together. So this changes no behaviour\n * today. It makes the equivalence guaranteed rather than accidental — it holds\n * only because that context hardcodes `hasWriteAccess` and `hasSecretAccess`\n * to true, which is what makes `checkRuleOfTwo` fire at tier 3+. Make either\n * conditional and RULE_OF_TWO stops firing while the other two blocking rules\n * still do, and a review would post against `allowed: false`.\n *\n * Returns `undefined` when posting may proceed. Rule of Two keeps its own\n * label: it is the distinctive condition (untrusted input + write access +\n * secrets at once) and reads very differently from a trust-tier block.\n */\nexport function reviewPostingBlock(\n verdict: ReviewPostingVerdict\n): { label: string; reason: string } | undefined {\n if (verdict.allowed) return undefined;\n const label = verdict.hasRuleOfTwoViolation ? 'Rule of Two' : 'Policy gate';\n return { label, reason: `${label}: ${verdict.violations.map((v) => v.rule).join(', ')}` };\n}\n","/**\n * Review coverage calculation and statistics rendering.\n *\n * @module dogfooding/pr-review-stats\n */\n\nimport type {\n ExpertReviewResult,\n PRMetadata,\n PRReviewDraft,\n ReviewSeverity,\n} from './pr-review-types.js';\nimport { SEVERITY_EMOJI } from './pr-review-types.js';\n\n/** Calculates how much of the fetched PR was supplied to a successful expert. */\nexport function getFileReviewCoverage(\n pr: PRMetadata,\n reviews: readonly ExpertReviewResult[],\n filesIncluded: number\n): Pick<PRReviewDraft, 'totalFiles' | 'filesWithPatch' | 'filesReviewed' | 'reviewCoverage'> {\n const totalFiles = pr.files.length;\n const filesWithPatch = pr.files.filter((file) => file.patch !== undefined).length;\n const hasSuccessfulReview = reviews.some((review) => review.errored !== true);\n const filesReviewed = hasSuccessfulReview ? filesIncluded : 0;\n const reviewCoverage =\n filesReviewed === 0 ? 'none' : filesReviewed === totalFiles ? 'full' : 'partial';\n return { totalFiles, filesWithPatch, filesReviewed, reviewCoverage };\n}\n\n/** Formats the measured file-review coverage without losing the PR total. */\nexport function formatFileReviewCoverage(result: PRReviewDraft): string {\n return `${String(result.filesReviewed)} of ${String(result.totalFiles)} files reviewed (${result.reviewCoverage})`;\n}\n\n/** Renders the collapsible statistics section in a GitHub review comment. */\nexport function formatReviewStats(result: PRReviewDraft): string {\n const total = Object.values(result.findingsBySeverity).reduce((a, b) => a + b, 0);\n const parts: string[] = [];\n for (const severity of ['critical', 'high', 'medium', 'low', 'info'] as ReviewSeverity[]) {\n const count = result.findingsBySeverity[severity];\n if (count > 0) parts.push(`${SEVERITY_EMOJI[severity]} ${String(count)} ${severity}`);\n }\n\n return `<details>\n<summary>Review Statistics (${String(total)} findings)</summary>\n\n- Experts: ${String(result.expertCount)}\n- Files: ${formatFileReviewCoverage(result)}\n- Consensus: ${(result.consensusScore * 100).toFixed(0)}%\n- Duration: ${String(result.totalDurationMs)}ms\n- Findings: ${parts.join(', ') || 'none'}\n\n</details>`;\n}\n"],"mappings":"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","names":[]}
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  DEFAULT_TASK_TTL_MS,
23
23
  clampTaskTtl
24
- } from "./chunk-TCUEFOPW.js";
24
+ } from "./chunk-JA2ZRSRP.js";
25
25
  import {
26
26
  executeExpert
27
27
  } from "./chunk-CN3VAMQD.js";
@@ -48,7 +48,7 @@ import {
48
48
  simulationDeniedResult,
49
49
  toJobSummary,
50
50
  writeJobCancelled
51
- } from "./chunk-M7TUO6O7.js";
51
+ } from "./chunk-MEZUIN3R.js";
52
52
  import {
53
53
  allOf
54
54
  } from "./chunk-OQB3XC5O.js";
@@ -67,7 +67,7 @@ import {
67
67
  } from "./chunk-OYTRYITB.js";
68
68
  import {
69
69
  withPrerequisite
70
- } from "./chunk-3CHPU46R.js";
70
+ } from "./chunk-6YP5DJUF.js";
71
71
  import {
72
72
  NOOP_NOTIFIER,
73
73
  RateLimiter,
@@ -92,7 +92,7 @@ import {
92
92
  withAuditTrail,
93
93
  withProgressHeartbeat,
94
94
  wrapToolWithTimeout
95
- } from "./chunk-ZFZJ6LTJ.js";
95
+ } from "./chunk-FAI5Z3TK.js";
96
96
  import {
97
97
  createAuditTrail,
98
98
  createGraphAuditBridge,
@@ -10302,7 +10302,8 @@ var Orchestrator = class extends BaseAgent {
10302
10302
  /** Execute a task by analyzing, decomposing (if needed), and coordinating. */
10303
10303
  async executeTask(task) {
10304
10304
  const startTime = getTimeProvider().now();
10305
- const analysisResult = await this.analyzeTask(task);
10305
+ const usage = { tokensUsed: 0, tokensMeasured: false };
10306
+ const analysisResult = await this.analyzeTaskWithUsage(task, usage);
10306
10307
  if (!analysisResult.ok) return err(analysisResult.error);
10307
10308
  const analysis = analysisResult.value;
10308
10309
  this.lastAnalysis = analysis;
@@ -10313,7 +10314,7 @@ var Orchestrator = class extends BaseAgent {
10313
10314
  });
10314
10315
  let subtasks = [];
10315
10316
  if (analysis.needsDecomposition) {
10316
- const decomposeResult = await this.decomposeTask(task, analysis);
10317
+ const decomposeResult = await this.decomposeTaskWithUsage(task, analysis, usage);
10317
10318
  if (!decomposeResult.ok) return err(decomposeResult.error);
10318
10319
  subtasks = decomposeResult.value;
10319
10320
  }
@@ -10326,7 +10327,8 @@ var Orchestrator = class extends BaseAgent {
10326
10327
  output: output2,
10327
10328
  metadata: {
10328
10329
  durationMs: getTimeProvider().now() - startTime,
10329
- tokensUsed: 0,
10330
+ tokensUsed: usage.tokensUsed,
10331
+ tokensMeasured: usage.tokensMeasured,
10330
10332
  toolsUsed: [],
10331
10333
  model: "tech-lead-orchestration",
10332
10334
  ...executedCli === void 0 ? { executedCliSource: "unknown" } : { executedCli, executedCliSource: "executed" }
@@ -10341,6 +10343,9 @@ ${task.description}` }];
10341
10343
  }
10342
10344
  /** Analyze a task to understand its complexity and requirements. */
10343
10345
  async analyzeTask(task) {
10346
+ return this.analyzeTaskWithUsage(task, { tokensUsed: 0, tokensMeasured: false });
10347
+ }
10348
+ async analyzeTaskWithUsage(task, usage) {
10344
10349
  if (this.adapter === void 0) {
10345
10350
  return ok(heuristicAnalysis(task, this.orchestratorOptions));
10346
10351
  }
@@ -10354,6 +10359,10 @@ ${task.description}` }],
10354
10359
  };
10355
10360
  const result = await this.complete(request);
10356
10361
  if (!result.ok) return err(result.error);
10362
+ if (result.value.usage !== void 0) {
10363
+ usage.tokensUsed += result.value.usage.totalTokens;
10364
+ usage.tokensMeasured = true;
10365
+ }
10357
10366
  const parseResult2 = this.parseJson(
10358
10367
  extractTextContent(result.value.content),
10359
10368
  TaskAnalysisSchema
@@ -10368,6 +10377,9 @@ ${task.description}` }],
10368
10377
  }
10369
10378
  /** Decompose a task into subtasks. */
10370
10379
  async decomposeTask(task, analysis) {
10380
+ return this.decomposeTaskWithUsage(task, analysis, { tokensUsed: 0, tokensMeasured: false });
10381
+ }
10382
+ async decomposeTaskWithUsage(task, analysis, usage) {
10371
10383
  if (this.adapter === void 0) {
10372
10384
  return ok(heuristicDecomposition(task, analysis, this.orchestratorOptions.maxSubtasks));
10373
10385
  }
@@ -10386,6 +10398,10 @@ Max: ${String(this.orchestratorOptions.maxSubtasks)}`
10386
10398
  };
10387
10399
  const result = await this.complete(request);
10388
10400
  if (!result.ok) return err(result.error);
10401
+ if (result.value.usage !== void 0) {
10402
+ usage.tokensUsed += result.value.usage.totalTokens;
10403
+ usage.tokensMeasured = true;
10404
+ }
10389
10405
  try {
10390
10406
  const rawText = extractTextContent(result.value.content);
10391
10407
  const fenced = extractCodeBlock(rawText);
@@ -12017,13 +12033,24 @@ function applySecOptionalFields(result, p) {
12017
12033
  if (isSecStringArray(p["recommendations"])) result.recommendations = p["recommendations"];
12018
12034
  if (isSecStringArray(p["warnings"])) result.warnings = p["warnings"];
12019
12035
  }
12020
- function buildSecurityCore(p, validVulns, calculateScore2) {
12021
- const score = p["securityScore"];
12036
+ function coverageFor(rejectedFindings, validFindings) {
12037
+ if (rejectedFindings === 0) return "complete";
12038
+ return validFindings === 0 ? "unmeasured" : "partial";
12039
+ }
12040
+ function scoreFor(suppliedScore, coverage, validVulns, calculateScore2) {
12041
+ if (coverage === "unmeasured") return 0;
12042
+ if (coverage === "partial") return calculateScore2(validVulns);
12043
+ return typeof suppliedScore === "number" && suppliedScore >= 0 && suppliedScore <= 100 ? suppliedScore : calculateScore2(validVulns);
12044
+ }
12045
+ function buildSecurityCore(p, validVulns, rejectedFindings, calculateScore2) {
12022
12046
  const conf = p["confidence"];
12047
+ const findingsCoverage = coverageFor(rejectedFindings, validVulns.length);
12048
+ const securityScore = scoreFor(p["securityScore"], findingsCoverage, validVulns, calculateScore2);
12023
12049
  return {
12024
12050
  content: typeof p["content"] === "string" ? p["content"] : "Security analysis completed",
12025
12051
  vulnerabilities: validVulns,
12026
- securityScore: typeof score === "number" && score >= 0 && score <= 100 ? score : calculateScore2(validVulns),
12052
+ securityScore,
12053
+ findingsCoverage,
12027
12054
  confidence: typeof conf === "number" && conf >= 0 && conf <= 1 ? conf : 0.7
12028
12055
  };
12029
12056
  }
@@ -12033,8 +12060,12 @@ function parseSecurityResult(text, calculateScore2, validator) {
12033
12060
  const rawParsed = JSON.parse(jsonText);
12034
12061
  if (!isSecPlainObject(rawParsed)) throw new Error("Parsed value is not a plain object");
12035
12062
  const vulnCandidates = Array.isArray(rawParsed["vulnerabilities"]) ? rawParsed["vulnerabilities"] : [];
12036
- const validVulns = vulnCandidates.map((v) => validator(v)).filter((r) => r.success).map((r) => r.data);
12037
- const result = buildSecurityCore(rawParsed, validVulns, calculateScore2);
12063
+ const validationResults = vulnCandidates.map((v) => validator(v));
12064
+ const validVulns = validationResults.flatMap(
12065
+ (result2) => result2.success && result2.data !== void 0 ? [result2.data] : []
12066
+ );
12067
+ const rejectedFindings = vulnCandidates.length - validVulns.length;
12068
+ const result = buildSecurityCore(rawParsed, validVulns, rejectedFindings, calculateScore2);
12038
12069
  applySecOptionalFields(result, rawParsed);
12039
12070
  return result;
12040
12071
  } catch {
@@ -12044,6 +12075,7 @@ function parseSecurityResult(text, calculateScore2, validator) {
12044
12075
  content: text,
12045
12076
  vulnerabilities: heuristicVulns,
12046
12077
  securityScore: score,
12078
+ findingsCoverage: "complete",
12047
12079
  confidence: heuristicVulns.length > 0 ? 0.5 : 0.3
12048
12080
  };
12049
12081
  }
@@ -12101,6 +12133,7 @@ Analyze for security vulnerabilities and provide findings in the specified JSON
12101
12133
  content: "Heuristic security analysis. Model adapter required for comprehensive review.",
12102
12134
  vulnerabilities,
12103
12135
  securityScore,
12136
+ findingsCoverage: "complete",
12104
12137
  recommendations: generateHeuristicRecommendations2(vulnerabilities),
12105
12138
  warnings: generateSecurityWarnings(vulnerabilities),
12106
12139
  confidence: vulnerabilities.length > 0 ? 0.6 : 0.4
@@ -46047,7 +46080,7 @@ function buildVotingInput(plan, config) {
46047
46080
  }
46048
46081
  async function executeSingleVote(plan, config, log) {
46049
46082
  try {
46050
- const { executeVoting } = await import("./consensus-vote-NQPSFVEF.js");
46083
+ const { executeVoting } = await import("./consensus-vote-4SBBG2AZ.js");
46051
46084
  const input = buildVotingInput(plan, config);
46052
46085
  const result = await executeVoting(input, log);
46053
46086
  return parseVotingResult(result);
@@ -47819,7 +47852,7 @@ ${contextBlock}`;
47819
47852
  const strategy = config.votingStrategy ?? "higher_order";
47820
47853
  await postProgress(config, "Vote", `Running consensus with ${strategy} strategy...`);
47821
47854
  try {
47822
- const { executeVoting } = await import("./consensus-vote-NQPSFVEF.js");
47855
+ const { executeVoting } = await import("./consensus-vote-4SBBG2AZ.js");
47823
47856
  const votingResult = await executeVoting(
47824
47857
  {
47825
47858
  proposal: buildVoteProposal(plan, research),
@@ -56504,4 +56537,4 @@ export {
56504
56537
  shutdownFeedbackSubscriber,
56505
56538
  createEventBusBridge
56506
56539
  };
56507
- //# sourceMappingURL=chunk-TBOM7PJ7.js.map
56540
+ //# sourceMappingURL=chunk-AZOIWYKL.js.map