@effect-agent/pr-review 0.1.0-beta.12 → 0.1.0-beta.14
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 +53 -14
- package/dist/action.d.mts +13 -3
- package/dist/action.mjs +35 -5
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{fan-out-TrA9EUCr.d.mts → fan-out-DBHPcJwC.d.mts} +133 -35
- package/dist/{github-5TCFrxfX.mjs → github-mmanX6hk.mjs} +204 -40
- package/dist/github-mmanX6hk.mjs.map +1 -0
- package/dist/index.d.mts +90 -4
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/logging-Q4j0oub-.mjs +75 -0
- package/dist/logging-Q4j0oub-.mjs.map +1 -0
- package/dist/{providers-DobNWMUn.mjs → providers-DD2GdrXQ.mjs} +387 -26
- package/dist/providers-DD2GdrXQ.mjs.map +1 -0
- package/dist/testing.d.mts +2 -1
- package/dist/testing.mjs +23 -15
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +60 -2
- package/src/index.ts +2 -0
- package/src/internal/action-entry.ts +2 -0
- package/src/internal/coverage.ts +42 -4
- package/src/internal/diff.ts +59 -0
- package/src/internal/fan-out.ts +23 -3
- package/src/internal/fingerprint.ts +1 -1
- package/src/internal/fixtures.ts +15 -4
- package/src/internal/github-env.ts +8 -1
- package/src/internal/github.ts +73 -12
- package/src/internal/logging.ts +124 -0
- package/src/internal/progress.ts +433 -0
- package/src/internal/render.ts +251 -19
- package/src/internal/retirement.ts +5 -1
- package/src/internal/review-agent.ts +84 -10
- package/src/internal/review-state.ts +21 -1
- package/src/internal/review-units.ts +17 -11
- package/src/internal/run.ts +33 -2
- package/dist/github-5TCFrxfX.mjs.map +0 -1
- package/dist/providers-DobNWMUn.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, DateTime, Effect, Encoding, Layer, Option, Redacted, Result, Schema } from "effect";
|
|
2
|
-
import { Agent, AgentPolicy, Subagent, SubagentPolicy, SubagentRuntime, ToolExecutionClass } from "effect-agent";
|
|
2
|
+
import { Agent, AgentPolicy, Subagent, SubagentPolicy, SubagentRuntime, ToolExecutionClass, ToolResultBounds } from "effect-agent";
|
|
3
3
|
import { Tool, Toolkit } from "effect/unstable/ai";
|
|
4
4
|
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
5
5
|
//#region src/internal/diff.ts
|
|
@@ -24,8 +24,57 @@ var ChangedFile = class extends Schema.Class("@effect-agent/pr-review/ChangedFil
|
|
|
24
24
|
/** Present for renames/copies: the path the file previously had. */
|
|
25
25
|
previousPath: Schema.optionalKey(ChangedPath),
|
|
26
26
|
/** Unified-diff hunks; absent for binary or oversized files. */
|
|
27
|
-
patch: Schema.optionalKey(Schema.String)
|
|
27
|
+
patch: Schema.optionalKey(Schema.String),
|
|
28
|
+
/**
|
|
29
|
+
* Bounded UTF-8 content used only when the provider omitted `patch`.
|
|
30
|
+
* Modified files require both sides; additions require head content and
|
|
31
|
+
* deletions require base content. These values are review evidence, never
|
|
32
|
+
* GitHub inline-comment anchors.
|
|
33
|
+
*/
|
|
34
|
+
reviewBaseContent: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(2e5))),
|
|
35
|
+
reviewHeadContent: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(2e5)))
|
|
28
36
|
}) {};
|
|
37
|
+
/** Complete rendered fallback evidence must fit one ordinary model context. */
|
|
38
|
+
const MAX_REVIEW_CONTENT_CHARS = 22e4;
|
|
39
|
+
/**
|
|
40
|
+
* Render complete patchless evidence, or refuse it when a required side is
|
|
41
|
+
* absent or B/H annotation would exceed the model-facing bound. Callers use
|
|
42
|
+
* this same value for planning and tool output so truncated fallback evidence
|
|
43
|
+
* can never count as complete coverage.
|
|
44
|
+
*/
|
|
45
|
+
const renderReviewContent = (file) => {
|
|
46
|
+
if (file.patch !== void 0) return void 0;
|
|
47
|
+
const includeBase = file.status !== "added";
|
|
48
|
+
const includeHead = file.status !== "removed";
|
|
49
|
+
const sections = ["[GitHub omitted the unified diff. B/H lines below are bounded full-file review content, not valid inline-comment anchors. Report defects from this evidence as non-anchored concerns.]"];
|
|
50
|
+
let renderedLength = sections[0]?.length ?? 0;
|
|
51
|
+
const append = (part) => {
|
|
52
|
+
const nextLength = renderedLength + 1 + part.length;
|
|
53
|
+
if (nextLength > 22e4) return false;
|
|
54
|
+
sections.push(part);
|
|
55
|
+
renderedLength = nextLength;
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
const appendSide = (side, header, content) => {
|
|
59
|
+
if (!append(header)) return false;
|
|
60
|
+
const lines = content.split("\n");
|
|
61
|
+
for (let index = 0; index < lines.length; index += 1) if (!append(`${side}${index + 1} ${lines[index] ?? ""}`)) return false;
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
if (includeBase) {
|
|
65
|
+
if (file.reviewBaseContent === void 0) return void 0;
|
|
66
|
+
if (!appendSide("B", "[BASE VERSION]", file.reviewBaseContent)) return void 0;
|
|
67
|
+
}
|
|
68
|
+
if (includeHead) {
|
|
69
|
+
if (file.reviewHeadContent === void 0) return void 0;
|
|
70
|
+
if (!appendSide("H", "[HEAD VERSION]", file.reviewHeadContent)) return void 0;
|
|
71
|
+
}
|
|
72
|
+
return sections.join("\n");
|
|
73
|
+
};
|
|
74
|
+
/** Whether complete patchless evidence fits the model-facing review bound. */
|
|
75
|
+
const hasReviewableContent = (file) => renderReviewContent(file) !== void 0;
|
|
76
|
+
/** Whether the reviewer has either a real patch or bounded textual fallback evidence. */
|
|
77
|
+
const isReviewableFile = (file) => file.patch !== void 0 || hasReviewableContent(file);
|
|
29
78
|
const HUNK_HEADER = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
30
79
|
/**
|
|
31
80
|
* Parse unified-diff hunk text into coordinate-tagged lines. Lines outside a
|
|
@@ -188,6 +237,8 @@ const MAX_FINDINGS = 20;
|
|
|
188
237
|
const MAX_CONCERNS = 10;
|
|
189
238
|
/** Annotated patches larger than this are truncated with an explicit marker. */
|
|
190
239
|
const MAX_PATCH_CHARS = 6e4;
|
|
240
|
+
/** The encoded Tool result must retain one complete bounded content fallback. */
|
|
241
|
+
const REVIEW_TOOL_RESULT_MAX_BYTES = 2 * 1024 * 1024;
|
|
191
242
|
/** One `read_file` slice never exceeds this many lines. */
|
|
192
243
|
const MAX_SLICE_LINES = 1e3;
|
|
193
244
|
const DEFAULT_SLICE_LINES = 400;
|
|
@@ -196,7 +247,9 @@ var ChangedFileSummary = class extends Schema.Class("@effect-agent/pr-review/Cha
|
|
|
196
247
|
status: ChangedFileStatus,
|
|
197
248
|
additions: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
198
249
|
deletions: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
199
|
-
hasTextualDiff: Schema.Boolean
|
|
250
|
+
hasTextualDiff: Schema.Boolean,
|
|
251
|
+
/** True when a missing patch was recovered as bounded UTF-8 base/head content. */
|
|
252
|
+
hasReviewableContent: Schema.Boolean
|
|
200
253
|
}) {};
|
|
201
254
|
var ChangedFilesView = class extends Schema.Class("@effect-agent/pr-review/ChangedFilesView")({
|
|
202
255
|
totalFiles: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
@@ -219,16 +272,23 @@ var FileDiffQuery = class extends Schema.Class("@effect-agent/pr-review/FileDiff
|
|
|
219
272
|
var FileDiffView = class extends Schema.Class("@effect-agent/pr-review/FileDiffView")({
|
|
220
273
|
path: ChangedPath,
|
|
221
274
|
status: ChangedFileStatus,
|
|
275
|
+
reviewMode: Schema.Literals([
|
|
276
|
+
"diff",
|
|
277
|
+
"content",
|
|
278
|
+
"unavailable"
|
|
279
|
+
]),
|
|
222
280
|
/**
|
|
223
281
|
* The unified diff with explicit RIGHT-side line numbers: `R<n>` marks a
|
|
224
282
|
* line present in the new file version (only those may anchor findings);
|
|
225
|
-
* `-` marks removed lines.
|
|
283
|
+
* `-` marks removed lines. For content fallback, `B<n>` and `H<n>`
|
|
284
|
+
* identify base/head lines for reading only; they are never valid anchors.
|
|
285
|
+
* Empty only when neither a patch nor bounded textual content exists.
|
|
226
286
|
*/
|
|
227
287
|
annotatedPatch: Schema.String,
|
|
228
288
|
truncated: Schema.Boolean
|
|
229
289
|
}) {};
|
|
230
290
|
const ReadFileDiff = Tool.make("read_file_diff", {
|
|
231
|
-
description: "Read
|
|
291
|
+
description: "Read one changed file's review evidence. A normal unified diff marks valid anchors as R<number>. When GitHub omitted the diff, bounded base/head content is returned with B/H line labels for review but no valid inline anchors.",
|
|
232
292
|
parameters: FileDiffQuery,
|
|
233
293
|
success: FileDiffView,
|
|
234
294
|
failure: Schema.Union([PullRequestSourceFailure, ReviewInputViolation]),
|
|
@@ -275,7 +335,8 @@ const listChangedFilesHandler = (_query) => Effect.gen(function* () {
|
|
|
275
335
|
status: file.status,
|
|
276
336
|
additions: file.additions,
|
|
277
337
|
deletions: file.deletions,
|
|
278
|
-
hasTextualDiff: file.patch !== void 0
|
|
338
|
+
hasTextualDiff: file.patch !== void 0,
|
|
339
|
+
hasReviewableContent: hasReviewableContent(file)
|
|
279
340
|
}))
|
|
280
341
|
});
|
|
281
342
|
});
|
|
@@ -291,11 +352,14 @@ const readFileDiffHandler = (query) => Effect.gen(function* () {
|
|
|
291
352
|
input: relative,
|
|
292
353
|
reason: "Path is not part of this pull request's changeset."
|
|
293
354
|
});
|
|
294
|
-
const
|
|
295
|
-
const
|
|
355
|
+
const contentEvidence = renderReviewContent(file);
|
|
356
|
+
const reviewMode = file.patch !== void 0 ? "diff" : contentEvidence !== void 0 ? "content" : "unavailable";
|
|
357
|
+
const annotated = file.patch === void 0 ? contentEvidence ?? "" : annotatePatch(file.patch);
|
|
358
|
+
const truncated = reviewMode === "diff" && annotated.length > MAX_PATCH_CHARS;
|
|
296
359
|
return FileDiffView.make({
|
|
297
360
|
path: file.path,
|
|
298
361
|
status: file.status,
|
|
362
|
+
reviewMode,
|
|
299
363
|
annotatedPatch: truncated ? `${annotated.slice(0, MAX_PATCH_CHARS)}\n[diff truncated]` : annotated,
|
|
300
364
|
truncated
|
|
301
365
|
});
|
|
@@ -343,12 +407,30 @@ const FindingSeverity = Schema.Literals([
|
|
|
343
407
|
"important",
|
|
344
408
|
"nit"
|
|
345
409
|
]);
|
|
410
|
+
/**
|
|
411
|
+
* What kind of problem a finding names. Model-claimed like severity — it is a
|
|
412
|
+
* label for scanning a busy review, never an input to the check conclusion.
|
|
413
|
+
*/
|
|
414
|
+
const FindingCategory = Schema.Literals([
|
|
415
|
+
"correctness",
|
|
416
|
+
"security",
|
|
417
|
+
"concurrency",
|
|
418
|
+
"performance",
|
|
419
|
+
"resources",
|
|
420
|
+
"error-handling",
|
|
421
|
+
"testing",
|
|
422
|
+
"maintainability",
|
|
423
|
+
"style",
|
|
424
|
+
"docs"
|
|
425
|
+
]);
|
|
346
426
|
var ReviewFinding = class extends Schema.Class("@effect-agent/pr-review/ReviewFinding")({
|
|
347
427
|
path: ChangedPath,
|
|
348
428
|
/** 1-based line numbers in the NEW file version; must appear in the diff. */
|
|
349
429
|
startLine: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
350
430
|
endLine: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
351
431
|
severity: FindingSeverity,
|
|
432
|
+
/** Optional problem-kind label rendered next to the severity. */
|
|
433
|
+
category: Schema.optionalKey(FindingCategory),
|
|
352
434
|
title: Schema.NonEmptyString.check(Schema.isMaxLength(120)),
|
|
353
435
|
body: Schema.NonEmptyString.check(Schema.isMaxLength(2e3)),
|
|
354
436
|
/** Replacement for exactly lines startLine..endLine; omit when unsure. */
|
|
@@ -371,12 +453,26 @@ var ReviewConcern = class extends Schema.Class("@effect-agent/pr-review/ReviewCo
|
|
|
371
453
|
title: Schema.NonEmptyString.check(Schema.isMaxLength(120)),
|
|
372
454
|
body: Schema.NonEmptyString.check(Schema.isMaxLength(2e3))
|
|
373
455
|
}) {};
|
|
456
|
+
/** The per-entry walkthrough summary bound, and the entries bound (the changeset cap). */
|
|
457
|
+
const MAX_WALKTHROUGH_SUMMARY_CHARS = 240;
|
|
458
|
+
const MAX_WALKTHROUGH_ENTRIES = 300;
|
|
459
|
+
/**
|
|
460
|
+
* One reviewed file's one-sentence change summary. Rendered only when the
|
|
461
|
+
* path is actually part of the changeset — like finding anchors, walkthrough
|
|
462
|
+
* paths are validated host-side and invented ones are dropped.
|
|
463
|
+
*/
|
|
464
|
+
var WalkthroughEntry = class extends Schema.Class("@effect-agent/pr-review/WalkthroughEntry")({
|
|
465
|
+
path: ChangedPath,
|
|
466
|
+
summary: Schema.NonEmptyString.check(Schema.isMaxLength(240))
|
|
467
|
+
}) {};
|
|
374
468
|
var CodeReview = class extends Schema.Class("@effect-agent/pr-review/CodeReview")({
|
|
375
469
|
summary: Schema.NonEmptyString.check(Schema.isMaxLength(4e3)),
|
|
376
470
|
verdict: ReviewVerdict,
|
|
377
471
|
findings: Schema.Array(ReviewFinding).check(Schema.isMaxLength(20)),
|
|
378
472
|
/** Non-anchorable concerns; absent when the review raises none. */
|
|
379
|
-
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(10)))
|
|
473
|
+
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(10))),
|
|
474
|
+
/** Per-file change summaries; absent when the model provides none. */
|
|
475
|
+
walkthrough: Schema.optionalKey(Schema.Array(WalkthroughEntry).check(Schema.isMaxLength(300)))
|
|
380
476
|
}) {};
|
|
381
477
|
const resolveGuidance = (guidance, mission) => {
|
|
382
478
|
if (guidance === void 0) return [];
|
|
@@ -393,15 +489,16 @@ const makeReviewInstructions = (options = {}) => (mission) => {
|
|
|
393
489
|
mission.body.length > 0 ? `Author description:\n${mission.body}` : "The author provided no description.",
|
|
394
490
|
...resolveGuidance(options.guidance, mission),
|
|
395
491
|
"Work in this order:",
|
|
396
|
-
"1. Call list_changed_files once to see the changeset.",
|
|
397
|
-
"2. Call read_file_diff for every file you review.
|
|
398
|
-
"3. Call read_file when you need surrounding context the diff does not show. ONLY files
|
|
492
|
+
"1. Call list_changed_files once to see the changeset. That list is your COMPLETE review scope: in incremental reviews it is deliberately a subset of the pull request's full diff (totalFiles counts the whole pull request), and everything it omits was already reviewed or excluded.",
|
|
493
|
+
"2. Call read_file_diff for every file you review. A normal diff marks new-version anchors as R<number>; only those numbers are valid startLine/endLine values. When GitHub omitted a diff, the tool may return bounded base/head content marked B/H instead. Review that content, but report its defects as non-anchored concerns because B/H lines cannot anchor GitHub comments. Never anchor a finding to a removed (-), B, or H line.",
|
|
494
|
+
"3. Call read_file when you need surrounding context the diff does not show. ONLY listed files are readable — read_file_diff and read_file both return a failed result for any other path (an import, a neighbor, a file named in the description). Do not request or retry unlisted paths; reason from the visible diffs instead and note the gap honestly in your summary when it matters.",
|
|
399
495
|
"4. Review for real defects first: correctness, security, concurrency, resource leaks, error handling, API misuse. Style nits are least important. Do not praise; do not restate the diff.",
|
|
400
496
|
"When the diff adds or changes a test, check that it can actually fail: a test that would still pass with the bug present is theatre, not coverage. The usual tell is a loose assertion standing where an exact one belongs — >= or a truthiness check over an expected value, or a snapshot that absorbs whatever it is handed.",
|
|
401
497
|
"Go shallow only when the diff has no behavioral surface at all: doc typos, formatting, lockfile or generated-code regeneration, a mechanical rename. Line count is not the signal — a one-line change to auth, money, SQL, a comparison operator, or a config default is not trivial.",
|
|
402
498
|
"Drop bloat-shaped findings before reporting: defensive checks for cases that cannot happen, abstractions used once, comments restating obvious code, tests asserting tautologies, just-in-case guards. A finding must be sound, correct, and worth acting on; prefer an explicit keep over an invented finding.",
|
|
403
499
|
"5. After collecting anchored findings, deliberately scan for concerns with NO line to point at: deletion or cleanup plans for code the diff replaces, rollout or migration sequencing, coverage gaps the diff implies but does not add, scope questions only the author can answer. Report each as a \"concern\", never as a finding with an invented anchor; report none when none exist.",
|
|
404
|
-
|
|
500
|
+
`6. Write a walkthrough: for every file you reviewed, one factual sentence (<= 240 chars) describing what changed in that file — written for a reader scanning the pull request, never restating the diff line by line. Use only paths from list_changed_files; invented paths are dropped.`,
|
|
501
|
+
"7. Then return ONLY a JSON object — no Markdown fences, no prose before or after — exactly this shape: {\"summary\": <string, 1-3 paragraphs of overall assessment>, \"verdict\": <\"approve\" | \"comment\" | \"request-changes\">, \"findings\": [{\"path\": <string, a changed file path>, \"startLine\": <integer, an R-marked new-file line>, \"endLine\": <integer, >= startLine, same file, R-marked>, \"severity\": <\"blocking\" | \"important\" | \"nit\">, \"category\": <OPTIONAL: \"correctness\" | \"security\" | \"concurrency\" | \"performance\" | \"resources\" | \"error-handling\" | \"testing\" | \"maintainability\" | \"style\" | \"docs\">, \"title\": <string, <= 120 chars>, \"body\": <string, why it matters and what to do>, \"suggestion\": <string, OPTIONAL: replacement text for exactly lines startLine..endLine, ready to commit>}], \"concerns\": <array, OPTIONAL: [{\"severity\": <\"blocking\" | \"important\" | \"nit\">, \"title\": <string, <= 120 chars>, \"body\": <string>}], only for step-5 concerns with no valid anchor — never duplicate a finding here>, \"walkthrough\": <array, OPTIONAL: [{\"path\": <string, a changed file path>, \"summary\": <string, the step-6 sentence>}], one entry per reviewed file>}.",
|
|
405
502
|
`Report at most ${maxFindings} findings and at most 10 concerns; prefer the most important ones. An empty findings array with verdict "approve" is a valid review. Include "suggestion" only when you are confident the replacement compiles and preserves intent; its text must contain the full replacement for every line in the range and nothing else.`,
|
|
406
503
|
"Use verdict \"request-changes\" only when at least one finding or concern is \"blocking\". Line anchors you invent will be discarded, so copy R-numbers from read_file_diff output."
|
|
407
504
|
].join("\n");
|
|
@@ -414,8 +511,10 @@ const defaultReviewPolicy = AgentPolicy.make({
|
|
|
414
511
|
maxToolCalls: 24,
|
|
415
512
|
maxDuration: "8 minutes",
|
|
416
513
|
toolConcurrency: 2,
|
|
514
|
+
repeatedFailureLimit: 12,
|
|
417
515
|
tokenBudget: 3e5,
|
|
418
516
|
contextTokenLimit: 15e4,
|
|
517
|
+
toolResultBounds: ToolResultBounds.make({ maxBytes: REVIEW_TOOL_RESULT_MAX_BYTES }),
|
|
419
518
|
onExhaustion: "final-answer"
|
|
420
519
|
});
|
|
421
520
|
const PullRequestReviewer = Agent.define("pr-reviewer", {
|
|
@@ -456,7 +555,7 @@ var ReviewUnitPlan = class extends Schema.Class("@effect-agent/pr-review/ReviewU
|
|
|
456
555
|
/** True when the source returned fewer files than the pull request has. */
|
|
457
556
|
truncated: Schema.Boolean,
|
|
458
557
|
units: Schema.Array(ReviewUnit).check(Schema.isMaxLength(8)),
|
|
459
|
-
/** Changed files
|
|
558
|
+
/** Changed files with neither a textual diff nor bounded base/head text. */
|
|
460
559
|
undiffablePaths: Schema.Array(ChangedPath).check(Schema.isMaxLength(300)),
|
|
461
560
|
/**
|
|
462
561
|
* Diffable files beyond the fan-out capacity (MAX_REVIEW_UNITS units of
|
|
@@ -465,7 +564,11 @@ var ReviewUnitPlan = class extends Schema.Class("@effect-agent/pr-review/ReviewU
|
|
|
465
564
|
*/
|
|
466
565
|
unassignedPaths: Schema.Array(ChangedPath).check(Schema.isMaxLength(300))
|
|
467
566
|
}) {};
|
|
468
|
-
const fileCost = (file) =>
|
|
567
|
+
const fileCost = (file) => {
|
|
568
|
+
const contentChars = (file.reviewBaseContent?.length ?? 0) + (file.reviewHeadContent?.length ?? 0);
|
|
569
|
+
const contentWeight = Math.ceil(contentChars / 200);
|
|
570
|
+
return file.additions + file.deletions + contentWeight + FILE_OVERHEAD_LINES;
|
|
571
|
+
};
|
|
469
572
|
const unitOf = (index, files) => ReviewUnit.make({
|
|
470
573
|
unitId: `unit-${String(index + 1).padStart(3, "0")}`,
|
|
471
574
|
paths: files.map((file) => file.path),
|
|
@@ -479,22 +582,24 @@ const unitOf = (index, files) => ReviewUnit.make({
|
|
|
479
582
|
* then packed greedily in that order under the soft changed-line budget and
|
|
480
583
|
* the hard per-unit file bound. Capacity is finite and explicit:
|
|
481
584
|
*
|
|
482
|
-
* - files without a textual diff are
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
* -
|
|
585
|
+
* - files without a textual diff are still delegated when the source
|
|
586
|
+
* recovered complete bounded UTF-8 base/head content. Findings from that
|
|
587
|
+
* evidence cannot anchor inline and are reported as concerns;
|
|
588
|
+
* - files with neither form of textual evidence surface in
|
|
589
|
+
* `undiffablePaths` instead of laundering missing coverage;
|
|
590
|
+
* - reviewable files beyond `MAX_REVIEW_UNITS` full units surface in
|
|
486
591
|
* `unassignedPaths` so the review can report them as unreviewed, never
|
|
487
592
|
* silently truncated.
|
|
488
593
|
*/
|
|
489
594
|
const planReviewUnits = (files, options) => {
|
|
490
595
|
const ordered = [...files].sort((left, right) => left.path < right.path ? -1 : 1);
|
|
491
|
-
const
|
|
492
|
-
const undiffable = ordered.filter((file) => file
|
|
596
|
+
const reviewable = ordered.filter(isReviewableFile);
|
|
597
|
+
const undiffable = ordered.filter((file) => !isReviewableFile(file));
|
|
493
598
|
const groups = [];
|
|
494
599
|
const unassigned = [];
|
|
495
600
|
let current = [];
|
|
496
601
|
let currentCost = 0;
|
|
497
|
-
for (const file of
|
|
602
|
+
for (const file of reviewable) {
|
|
498
603
|
const cost = fileCost(file);
|
|
499
604
|
if (current.length >= 12 || current.length > 0 && currentCost + cost > 800) {
|
|
500
605
|
groups.push(current);
|
|
@@ -573,7 +678,9 @@ var FileReviewReport = class extends Schema.Class("@effect-agent/pr-review/FileR
|
|
|
573
678
|
unitId: ReviewUnitId,
|
|
574
679
|
findings: Schema.Array(ReviewFinding).check(Schema.isMaxLength(8)),
|
|
575
680
|
/** Unit-scoped concerns with no diff line to anchor to. */
|
|
576
|
-
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(3)))
|
|
681
|
+
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(3))),
|
|
682
|
+
/** One-sentence per-file change summaries for the merged walkthrough. */
|
|
683
|
+
fileSummaries: Schema.optionalKey(Schema.Array(WalkthroughEntry).check(Schema.isMaxLength(12)))
|
|
577
684
|
}) {};
|
|
578
685
|
const staticGuidanceLines = (guidance) => {
|
|
579
686
|
if (guidance === void 0) return [];
|
|
@@ -584,12 +691,13 @@ const makeFileReviewerInstructions = (options = {}) => (brief) => [
|
|
|
584
691
|
`You are a code reviewer for one unit of a pull request: unit ${brief.unitId}, covering exactly these changed files: ${brief.paths.join(", ")}. Focus: ${brief.focus}.`,
|
|
585
692
|
...staticGuidanceLines(options.guidance),
|
|
586
693
|
"Work in this order:",
|
|
587
|
-
"1. Call read_file_diff for every file in your unit.
|
|
694
|
+
"1. Call read_file_diff for every file in your unit. A normal diff marks new-version anchors as R<number>; only those numbers are valid startLine/endLine values. When GitHub omitted a diff, the tool may return bounded base/head content marked B/H instead. Review that content, but report its defects as non-anchored concerns because B/H lines cannot anchor GitHub comments. Never anchor a finding to a removed (-), B, or H line.",
|
|
588
695
|
"2. Call read_file when you need surrounding context the diff does not show. ONLY files in the changeset are readable: a request for any other path (an import, a neighbor, a config) returns a failed result — do not retry it; reason from the diff instead and note the gap in your report when it matters.",
|
|
589
696
|
"3. Review for real defects first: correctness, security, concurrency, resource leaks, error handling, API misuse. Style nits are least important. Do not praise; do not restate the diff.",
|
|
590
697
|
"When the diff adds or changes a test, check that it can actually fail: a test that would still pass with the bug present is theatre, not coverage. The usual tell is a loose assertion standing where an exact one belongs — >= or a truthiness check over an expected value, or a snapshot that absorbs whatever it is handed.",
|
|
591
698
|
"Drop bloat-shaped findings before reporting: defensive checks for cases that cannot happen, abstractions used once, comments restating obvious code, tests asserting tautologies, just-in-case guards. A finding must be sound, correct, and worth acting on; prefer an explicit keep over an invented finding.",
|
|
592
|
-
`4.
|
|
699
|
+
`4. For every file in your unit, write one factual sentence (<= 240 chars) describing what changed in that file — for a reader scanning the pull request, never a line-by-line restatement.`,
|
|
700
|
+
`5. Then return ONLY a JSON object — no Markdown fences, no prose before or after — exactly this shape: {"unitId": ${JSON.stringify(brief.unitId)}, "findings": [{"path": <string, a file in your unit>, "startLine": <integer, an R-marked new-file line>, "endLine": <integer, >= startLine, same file, R-marked>, "severity": <"blocking" | "important" | "nit">, "category": <OPTIONAL: "correctness" | "security" | "concurrency" | "performance" | "resources" | "error-handling" | "testing" | "maintainability" | "style" | "docs">, "title": <string, <= 120 chars>, "body": <string, why it matters and what to do>, "suggestion": <string, OPTIONAL: replacement text for exactly lines startLine..endLine, ready to commit>}], "concerns": <array, OPTIONAL: [{"severity": <"blocking" | "important" | "nit">, "title": <string, <= 120 chars>, "body": <string>}], only for concerns about YOUR unit's files with no valid line anchor (a missing cleanup, a coverage gap the diff implies, sequencing the diff leaves open) — never duplicate a finding here>, "fileSummaries": <array, OPTIONAL: [{"path": <string, a file in your unit>, "summary": <string, the step-4 sentence>}], one entry per file in your unit>}.`,
|
|
593
701
|
`Report at most 8 findings and at most 3 concerns; prefer the most important ones. An empty findings array is a valid report. Never report on files outside your unit. Line anchors you invent will be discarded, so copy R-numbers from read_file_diff output.`
|
|
594
702
|
].join("\n");
|
|
595
703
|
const fileReviewerInstructions = makeFileReviewerInstructions();
|
|
@@ -599,8 +707,10 @@ const defaultFileReviewerPolicy = AgentPolicy.make({
|
|
|
599
707
|
maxToolCalls: MAX_FILE_REVIEW_TOOL_CALLS,
|
|
600
708
|
maxDuration: "6 minutes",
|
|
601
709
|
toolConcurrency: 2,
|
|
710
|
+
repeatedFailureLimit: 12,
|
|
602
711
|
tokenBudget: 2e5,
|
|
603
712
|
contextTokenLimit: 15e4,
|
|
713
|
+
toolResultBounds: ToolResultBounds.make({ maxBytes: REVIEW_TOOL_RESULT_MAX_BYTES }),
|
|
604
714
|
onExhaustion: "fail"
|
|
605
715
|
});
|
|
606
716
|
/** The model-decoded delegation parameters: which unit to review. */
|
|
@@ -613,7 +723,9 @@ var FileReviewUnitResult = class extends Schema.Class("@effect-agent/pr-review/F
|
|
|
613
723
|
unitId: ReviewUnitId,
|
|
614
724
|
findings: Schema.Array(ReviewFinding).check(Schema.isMaxLength(8)),
|
|
615
725
|
/** Unit-scoped concerns with no diff line to anchor to. */
|
|
616
|
-
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(3)))
|
|
726
|
+
concerns: Schema.optionalKey(Schema.Array(ReviewConcern).check(Schema.isMaxLength(3))),
|
|
727
|
+
/** One-sentence per-file change summaries for the merged walkthrough. */
|
|
728
|
+
fileSummaries: Schema.optionalKey(Schema.Array(WalkthroughEntry).check(Schema.isMaxLength(12)))
|
|
617
729
|
}) {};
|
|
618
730
|
/**
|
|
619
731
|
* One unit's review failed: the child Run ended in a typed failure (policy
|
|
@@ -680,7 +792,8 @@ const makeFanOutReviewInstructions = (options = {}) => (mission) => {
|
|
|
680
792
|
"3. A delegation result with \"_tag\" is a FAILED unit. Never retry it; instead your summary MUST name it honestly, e.g. \"unit-002 unreviewed: AgentPolicyError\". The plan's undiffablePaths and unassignedPaths must also be named as not reviewed when present.",
|
|
681
793
|
`4. Merge the successful units' findings: drop duplicates sharing the same path and line range keeping the most severe, rank blocking > important > nit, and keep at most ${maxFindings} findings. Drop bloat-shaped findings during the merge — defensive checks for cases that cannot happen, abstractions used once, comments restating obvious code, tests asserting tautologies; children bias toward recommending changes, and a finding must be sound, correct, and worth acting on to survive.`,
|
|
682
794
|
`5. Merge the units' concerns the same way: drop duplicates keeping the most severe, and keep at most 10.`,
|
|
683
|
-
"6.
|
|
795
|
+
"6. Merge the units' fileSummaries into one walkthrough: copy each entry verbatim, one entry per file, dropping duplicate paths.",
|
|
796
|
+
"7. Then return ONLY a JSON object — no Markdown fences, no prose before or after — exactly this shape: {\"summary\": <string, 1-3 paragraphs of overall assessment, including every unreviewed unit or file>, \"verdict\": <\"approve\" | \"comment\" | \"request-changes\">, \"findings\": [{\"path\": <string>, \"startLine\": <integer>, \"endLine\": <integer>, \"severity\": <\"blocking\" | \"important\" | \"nit\">, \"category\": <string, OPTIONAL>, \"title\": <string, <= 120 chars>, \"body\": <string>, \"suggestion\": <string, OPTIONAL>}], \"concerns\": <array, OPTIONAL: [{\"severity\": <\"blocking\" | \"important\" | \"nit\">, \"title\": <string, <= 120 chars>, \"body\": <string>}], the merged unit concerns>, \"walkthrough\": <array, OPTIONAL: [{\"path\": <string>, \"summary\": <string>}], the merged fileSummaries>}. Copy findings (including \"category\" and \"suggestion\" when present), concerns, and walkthrough entries verbatim from the delegation results; never invent or edit anchors.",
|
|
684
797
|
"Use verdict \"request-changes\" only when at least one finding or concern is \"blocking\". An empty findings array with verdict \"approve\" is a valid review when every unit succeeded and found nothing."
|
|
685
798
|
].join("\n");
|
|
686
799
|
};
|
|
@@ -723,7 +836,8 @@ const makeFileReviewDelegation = (child) => Subagent.define("delegate_file_revie
|
|
|
723
836
|
projectResult: (report) => Effect.succeed(FileReviewUnitResult.make({
|
|
724
837
|
unitId: report.unitId,
|
|
725
838
|
findings: report.findings,
|
|
726
|
-
...report.concerns !== void 0 ? { concerns: report.concerns } : {}
|
|
839
|
+
...report.concerns !== void 0 ? { concerns: report.concerns } : {},
|
|
840
|
+
...report.fileSummaries !== void 0 ? { fileSummaries: report.fileSummaries } : {}
|
|
727
841
|
})),
|
|
728
842
|
policy: fileReviewPolicy
|
|
729
843
|
});
|
|
@@ -805,7 +919,7 @@ const SECTION = "";
|
|
|
805
919
|
* Canonical changeset encoding: sorted by path so provider ordering never
|
|
806
920
|
* matters, with every review-relevant field of every file.
|
|
807
921
|
*/
|
|
808
|
-
const canonicalChangeset = (files) => files.map((file) => `${file.path}${FIELD}${file.status}${FIELD}${String(file.additions)}${FIELD}${String(file.deletions)}${FIELD}${file.patch ?? ""}`).sort().join(RECORD);
|
|
922
|
+
const canonicalChangeset = (files) => files.map((file) => `${file.path}${FIELD}${file.status}${FIELD}${String(file.additions)}${FIELD}${String(file.deletions)}${FIELD}${file.patch ?? ""}${FIELD}${file.reviewBaseContent ?? ""}${FIELD}${file.reviewHeadContent ?? ""}`).sort().join(RECORD);
|
|
809
923
|
/**
|
|
810
924
|
* Fingerprint one review's complete input surface: the (already
|
|
811
925
|
* ignore-filtered) changeset plus the caller's prompt signature — the
|
|
@@ -1068,9 +1182,21 @@ var ReviewExecutionContext = class extends Context.Service()("@effect-agent/pr-r
|
|
|
1068
1182
|
const selectedPullRequestSourceLayer = (selection) => Layer.effect(PullRequestSource)(Effect.gen(function* () {
|
|
1069
1183
|
const source = yield* PullRequestSource;
|
|
1070
1184
|
const selectedPaths = new Set(selection.files.map((file) => file.path));
|
|
1185
|
+
const selectedFiles = source.changedFiles.pipe(Effect.map((fullFiles) => {
|
|
1186
|
+
const fullByPath = new Map(fullFiles.map((file) => [file.path, file]));
|
|
1187
|
+
return selection.files.map((file) => {
|
|
1188
|
+
if (file.patch !== void 0) return file;
|
|
1189
|
+
const full = fullByPath.get(file.path);
|
|
1190
|
+
return full === void 0 ? file : ChangedFile.make({
|
|
1191
|
+
...file,
|
|
1192
|
+
...full.reviewBaseContent === void 0 ? {} : { reviewBaseContent: full.reviewBaseContent },
|
|
1193
|
+
...full.reviewHeadContent === void 0 ? {} : { reviewHeadContent: full.reviewHeadContent }
|
|
1194
|
+
});
|
|
1195
|
+
});
|
|
1196
|
+
}));
|
|
1071
1197
|
return PullRequestSource.of({
|
|
1072
1198
|
metadata: source.metadata,
|
|
1073
|
-
changedFiles:
|
|
1199
|
+
changedFiles: selectedFiles,
|
|
1074
1200
|
anchorFiles: source.anchorFiles,
|
|
1075
1201
|
readFile: (path) => selectedPaths.has(path) ? source.readFile(path) : Effect.fail(ReviewInputViolation.make({
|
|
1076
1202
|
input: path,
|
|
@@ -1140,7 +1266,7 @@ const STATE_PATTERN = /<!-- effect-agent-pr-review state-v1:[A-Za-z0-9+/]+={0,2}
|
|
|
1140
1266
|
const RETIRED_ORIGINAL_PATTERN = /<!-- effect-agent-pr-review retired-original:start -->\n([\s\S]*?)\n<!-- effect-agent-pr-review retired-original:end -->/;
|
|
1141
1267
|
const MACHINE_COMMENT_PATTERN = new RegExp(`${REVIEW_METADATA_PATTERN.source}|${FINGERPRINT_PATTERN.source}|${STATE_PATTERN.source}`, "g");
|
|
1142
1268
|
const VERDICT_CALLOUT_PATTERN = /^(?:> \[!(?:CAUTION|IMPORTANT)\]\n> [^\n]*(?:\n> [^\n]*)*|> (?:ℹ️|✅)[^\n]*)\n*/;
|
|
1143
|
-
const INLINE_FINDING_TITLE_PATTERN = /^\*\*\[(?:🛑 blocking|⚠️ important|💅 nit)
|
|
1269
|
+
const INLINE_FINDING_TITLE_PATTERN = /^\*\*\[(?:🛑 blocking|⚠️ important|💅 nit)(?: · [a-z-]+)?\] ([^\n]+)\*\*$/;
|
|
1144
1270
|
const MAX_REVIEW_BODY_CHARS = 6e4;
|
|
1145
1271
|
/** The host-authored metadata marker is the authority gate for any edit. */
|
|
1146
1272
|
const hasReviewMetadataMarker = (body) => /<!-- effect-agent-pr-review metadata\n/.test(body);
|
|
@@ -1437,22 +1563,60 @@ const gitHubPullRequestSourceLayer = Layer.effect(PullRequestSource)(Effect.gen(
|
|
|
1437
1563
|
return all;
|
|
1438
1564
|
});
|
|
1439
1565
|
const metadata = yield* Effect.cached(fetchMetadata.pipe(Effect.provideService(HttpClient.HttpClient, client)));
|
|
1440
|
-
const
|
|
1441
|
-
const
|
|
1566
|
+
const rawFiles = yield* Effect.cached(fetchFiles.pipe(Effect.provideService(HttpClient.HttpClient, client)));
|
|
1567
|
+
const readRepositoryFile = (path, ref) => Effect.gen(function* () {
|
|
1442
1568
|
const relative = yield* normalizeRepoRelativePath(path);
|
|
1443
|
-
|
|
1569
|
+
const encodedPath = relative.split("/").map(encodeURIComponent).join("/");
|
|
1570
|
+
const buffer = yield* (yield* executeOk("readFile", withCommonHeaders(HttpClientRequest.get(`${target.apiUrl}/repos/${target.repository}/contents/${encodedPath}`).pipe(HttpClientRequest.accept("application/vnd.github.raw+json"), HttpClientRequest.setUrlParams({ ref })), target.token)).pipe(Effect.provideService(HttpClient.HttpClient, client))).arrayBuffer.pipe(Effect.mapError(failWith("readFile")));
|
|
1571
|
+
if (buffer.byteLength > 2e5) return yield* ReviewInputViolation.make({
|
|
1444
1572
|
input: relative,
|
|
1445
|
-
reason:
|
|
1573
|
+
reason: `File is larger than the ${MAX_FILE_CHARS}-byte read bound.`
|
|
1574
|
+
});
|
|
1575
|
+
const text = yield* Effect.try({
|
|
1576
|
+
try: () => new TextDecoder("utf-8", { fatal: true }).decode(buffer),
|
|
1577
|
+
catch: () => ReviewInputViolation.make({
|
|
1578
|
+
input: relative,
|
|
1579
|
+
reason: "File is not valid UTF-8 text."
|
|
1580
|
+
})
|
|
1581
|
+
});
|
|
1582
|
+
if (text.includes("\0")) return yield* ReviewInputViolation.make({
|
|
1583
|
+
input: relative,
|
|
1584
|
+
reason: "File contains binary NUL bytes."
|
|
1446
1585
|
});
|
|
1447
|
-
const head = yield* metadata;
|
|
1448
|
-
const encodedPath = relative.split("/").map(encodeURIComponent).join("/");
|
|
1449
|
-
const text = yield* (yield* executeOk("readFile", withCommonHeaders(HttpClientRequest.get(`${target.apiUrl}/repos/${target.repository}/contents/${encodedPath}`).pipe(HttpClientRequest.accept("application/vnd.github.raw+json"), HttpClientRequest.setUrlParams({ ref: head.headSha })), target.token)).pipe(Effect.provideService(HttpClient.HttpClient, client))).text.pipe(Effect.mapError(failWith("readFile")));
|
|
1450
1586
|
if (text.length > 2e5) return yield* ReviewInputViolation.make({
|
|
1451
1587
|
input: relative,
|
|
1452
1588
|
reason: `File is larger than the ${MAX_FILE_CHARS}-character read bound.`
|
|
1453
1589
|
});
|
|
1454
1590
|
return text;
|
|
1455
1591
|
});
|
|
1592
|
+
const changedFiles = yield* Effect.cached(Effect.gen(function* () {
|
|
1593
|
+
const [files, pullRequest] = yield* Effect.all([rawFiles, metadata]);
|
|
1594
|
+
return yield* Effect.forEach(files, (file) => {
|
|
1595
|
+
if (file.patch !== void 0) return Effect.succeed(file);
|
|
1596
|
+
const basePath = file.previousPath ?? file.path;
|
|
1597
|
+
const base = file.status === "added" ? Effect.succeed(Option.none()) : readRepositoryFile(basePath, pullRequest.baseSha ?? pullRequest.baseRef).pipe(Effect.option);
|
|
1598
|
+
const head = file.status === "removed" ? Effect.succeed(Option.none()) : readRepositoryFile(file.path, pullRequest.headSha).pipe(Effect.option);
|
|
1599
|
+
return Effect.all({
|
|
1600
|
+
base,
|
|
1601
|
+
head
|
|
1602
|
+
}).pipe(Effect.map(({ base, head }) => ChangedFile.make({
|
|
1603
|
+
...file,
|
|
1604
|
+
...Option.isSome(base) ? { reviewBaseContent: base.value } : {},
|
|
1605
|
+
...Option.isSome(head) ? { reviewHeadContent: head.value } : {}
|
|
1606
|
+
})));
|
|
1607
|
+
}, { concurrency: 4 });
|
|
1608
|
+
}));
|
|
1609
|
+
const readFile = (path) => Effect.gen(function* () {
|
|
1610
|
+
const relative = yield* normalizeRepoRelativePath(path);
|
|
1611
|
+
const file = (yield* changedFiles).find((candidate) => candidate.path === relative);
|
|
1612
|
+
if (file === void 0) return yield* ReviewInputViolation.make({
|
|
1613
|
+
input: relative,
|
|
1614
|
+
reason: "Path is not part of this pull request's changeset."
|
|
1615
|
+
});
|
|
1616
|
+
if (file.reviewHeadContent !== void 0) return file.reviewHeadContent;
|
|
1617
|
+
const head = yield* metadata;
|
|
1618
|
+
return yield* readRepositoryFile(relative, head.headSha);
|
|
1619
|
+
});
|
|
1456
1620
|
return PullRequestSource.of({
|
|
1457
1621
|
metadata,
|
|
1458
1622
|
changedFiles,
|
|
@@ -1671,6 +1835,6 @@ const fingerprintUnchanged = (current) => Effect.gen(function* () {
|
|
|
1671
1835
|
return Option.isSome(latest) && latest.value === current;
|
|
1672
1836
|
});
|
|
1673
1837
|
//#endregion
|
|
1674
|
-
export { FanOutReviewToolkit as $,
|
|
1838
|
+
export { FanOutReviewToolkit as $, ReadFile as $t, ReviewStateAuthenticator as A, isReviewableFile as An, ReviewUnit as At, selectedPullRequestSourceLayer as B, FileDiffView as Bt, MAX_REVIEW_STATE_MARKER_CHARS as C, ChangedFile as Cn, makeFanOutReviewInstructions as Ct, ReviewScopeMode as D, annotatePatch as Dn, MAX_MERGED_FINDINGS as Dt, ReviewMode as E, MAX_REVIEW_CONTENT_CHARS as En, mapFileReviewChildFailure as Et, buildProfileMission as F, rankAndDedupeFindings as Ft, webCryptoReviewStateAuthenticatorLayer as G, ListChangedFiles as Gt, toStoredFinding as H, FileSliceQuery as Ht, computeProfileFingerprint as I, ChangedFileSummary as It, extractFingerprint as J, MAX_FINDINGS as Jt, FINGERPRINT_MARKER_LENGTH as K, ListChangedFilesQuery as Kt, fromStoredConcern as L, ChangedFilesView as Lt, ReviewStateMarkerTooLarge as M, renderReviewContent as Mn, ReviewUnitPlan as Mt, StoredReviewConcern as N, UNIT_CHANGED_LINE_BUDGET as Nt, ReviewState as O, commentableLines as On, MAX_REVIEW_UNITS as Ot, StoredReviewFinding as P, planReviewUnits as Pt, FanOutCoordinatorToolkitLayer as Q, REVIEW_TOOL_RESULT_MAX_BYTES as Qt, fromStoredFinding as R, CodeReview as Rt, GitCommitSha as S, normalizeRepoRelativePath as Sn, fileReviewerInstructions as St, ReviewHeadComparison as T, ChangedPath as Tn, makeFileReviewerInstructions as Tt, unavailableReviewStateAuthenticatorLayer as U, FindingCategory as Ut, toStoredConcern as V, FileSlice as Vt, validateReviewState as W, FindingSeverity as Wt, DelegateFileReview as X, MAX_WALKTHROUGH_SUMMARY_CHARS as Xt, renderFingerprintMarker as Y, MAX_WALKTHROUGH_ENTRIES as Yt, FanOutCoordinatorToolkit as Z, PullRequestReviewer as Zt, ReviewRetirementHost as _, MAX_FILE_CHARS as _n, fanOutHandlersLayer as _t, PriorReviews as a, ReviewToolkitLayer as an, FileReviewToolkit as at, hasReviewMetadataMarker as b, PullRequestSourceFailure as bn, fileReviewDelegation as bt, fingerprintUnchanged as c, clampMaxFindings as cn, FileReviewUnitResult as ct, gitHubReviewPublisherLayer as d, makeReviewInstructions as dn, ListReviewUnitsQuery as dt, ReadFileDiff as en, FanOutReviewer as et, gitHubReviewRetirementHostLayer as f, readFileDiffHandler as fn, MAX_CHILD_CONCERNS as ft, ReviewRetirementFailure as g, MAX_CHANGED_FILES as gn, defaultFileReviewerPolicy as gt, RetirableReviewComment as h, reviewInstructions as hn, defaultFanOutPolicy as ht, PriorReviewLookupFailure as i, ReviewToolkit as in, FileReviewRequest as it, ReviewStateMarker as j, parsePatch as jn, ReviewUnitId as jt, ReviewStateAuthenticationFailure as k, hasReviewableContent as kn, MAX_UNIT_FILES as kt, gitHubPriorReviewsLayer as l, defaultReviewPolicy as ln, FileReviewer as lt, RetirableReview as m, resolveGuidance as mn, MAX_FILE_REVIEW_TOOL_CALLS as mt, GitHubApiFailure as n, ReviewFinding as nn, FileReviewDelegationFailure as nt, PublishedReview as o, ReviewVerdict as on, FileReviewToolkitLayer as ot, parseGitHubSubmittedAt as p, readFileHandler as pn, MAX_CHILD_FINDINGS as pt, computeChangesetFingerprint as q, MAX_CONCERNS as qt, GitHubReviewTarget as r, ReviewMission as rn, FileReviewReport as rt, ReviewPublisher as s, WalkthroughEntry as sn, FileReviewUnitFailed as st, DEFAULT_GITHUB_REVIEW_AUTHOR_LOGIN as t, ReviewConcern as tn, FileReviewBrief as tt, gitHubPullRequestSourceLayer as u, listChangedFilesHandler as un, ListReviewUnits as ut, ReviewRetirementReport as v, PullRequestMetadata as vn, fanOutHandlersLayerFor as vt, ReviewExecutionContext as w, ChangedFileStatus as wn, makeFanOutReviewSuite as wt, retireStaleReviews as x, ReviewInputViolation as xn, fileReviewPolicy as xt, decideReviewRetirement as y, PullRequestSource as yn, fanOutReviewInstructions as yt, selectReviewRange as z, FileDiffQuery as zt };
|
|
1675
1839
|
|
|
1676
|
-
//# sourceMappingURL=github-
|
|
1840
|
+
//# sourceMappingURL=github-mmanX6hk.mjs.map
|