@effect-agent/pr-review 0.1.0-beta.22 → 0.1.0-beta.24
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 +72 -48
- package/dist/action.d.mts +13 -13
- package/dist/action.mjs +42 -25
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{fan-out-n-00ppWr.d.mts → fan-out-Bi1v0VaU.d.mts} +314 -307
- package/dist/{github-BgtP7Rdv.mjs → github-C6jrBLA2.mjs} +763 -285
- package/dist/github-C6jrBLA2.mjs.map +1 -0
- package/dist/index.d.mts +84 -126
- package/dist/index.mjs +11 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{providers-BguZK4B_.mjs → providers-2Jao2ZAX.mjs} +173 -585
- package/dist/providers-2Jao2ZAX.mjs.map +1 -0
- package/dist/testing.d.mts +6 -15
- package/dist/testing.mjs +14 -42
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +58 -34
- package/src/internal/action-entry.ts +0 -1
- package/src/internal/coverage.ts +147 -548
- package/src/internal/factory.ts +33 -54
- package/src/internal/fan-out-scripted.ts +26 -80
- package/src/internal/fan-out.ts +669 -432
- package/src/internal/fingerprint.ts +16 -10
- package/src/internal/fixtures.ts +6 -0
- package/src/internal/github.ts +36 -7
- package/src/internal/profiles.ts +8 -8
- package/src/internal/render.ts +34 -45
- package/src/internal/retirement.ts +3 -1
- package/src/internal/review-state.ts +204 -89
- package/src/internal/review-units.ts +25 -0
- package/src/internal/run.ts +231 -174
- package/src/internal/source.ts +1 -1
- package/dist/github-BgtP7Rdv.mjs.map +0 -1
- package/dist/providers-BguZK4B_.mjs.map +0 -1
|
@@ -1,443 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $t as ReviewAssurance, A as ReviewState, B as fromStoredConcern, Dn as ReviewConcern, E as ReviewExecutionContext, En as ReadFileDiff, Fn as defaultReviewPolicy, G as toStoredConcern, Hn as resolveGuidance, K as toStoredFinding, Kn as PullRequestMetadata, L as StoredUnreviewedPass, On as ReviewFinding, Ot as runFanOutReview, Pn as clampMaxFindings, Q as computeProfileFingerprint, Tn as ReadFile, Tt as makeFileReviewerDefinition, V as fromStoredFinding, Xt as rankAndDedupeFindings, Yn as ReviewInputViolation, Yt as rankAndDedupeConcerns, Z as computeChangesetFingerprint, Zn as anchorViolation, an as fanOutInputCoverage, d as gitHubReviewPublisherLayer, en as ReviewCoverage, et as renderFingerprintMarker, f as gitHubReviewRetirementHostLayer, gn as ListChangedFiles, in as compatibilityCoverage, jn as ReviewToolkitLayer, kn as ReviewMission, l as gitHubPriorReviewsLayer, ln as CodeReview, n as GitHubApiFailure, nn as assessFlatReview, o as PublishedReview, qn as PullRequestSource, r as GitHubReviewTarget, s as ReviewPublisher, t as DEFAULT_GITHUB_REVIEW_AUTHOR_LOGIN, tn as ReviewInputCoverage, u as gitHubPullRequestSourceLayer, z as buildProfileMission, zn as makeReviewInstructions } from "./github-C6jrBLA2.mjs";
|
|
2
2
|
import { Config, Context, DateTime, Effect, FileSystem, Layer, Option, Ref, Schema } from "effect";
|
|
3
|
-
import { Agent, AgentPolicy, AgentRuntime, IdGenerator,
|
|
3
|
+
import { Agent, AgentPolicy, AgentRuntime, IdGenerator, UsageBudgetLimits, UsageTotals, getToolExecutionClass, makeUsageBudget, toRunBudgetHook } from "effect-agent";
|
|
4
4
|
import { Toolkit } from "effect/unstable/ai";
|
|
5
5
|
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
6
6
|
import { AnthropicClient, AnthropicLanguageModel } from "@effect/ai-anthropic";
|
|
7
7
|
import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai";
|
|
8
|
-
//#region src/internal/coverage.ts
|
|
9
|
-
const ReviewShape = Schema.Literals(["flat", "fan-out"]);
|
|
10
|
-
var FailedReviewUnit = class extends Schema.Class("@effect-agent/pr-review/FailedReviewUnit")({
|
|
11
|
-
unitId: Schema.NonEmptyString.check(Schema.isMaxLength(32)),
|
|
12
|
-
errorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256))
|
|
13
|
-
}) {};
|
|
14
|
-
/**
|
|
15
|
-
* Compatibility diagnostic retained for callers that consumed the original
|
|
16
|
-
* `coverage` field. New UI and state decisions use ReviewInputCoverage and
|
|
17
|
-
* ReviewAssurance directly.
|
|
18
|
-
*/
|
|
19
|
-
var ReviewCoverage = class extends Schema.Class("@effect-agent/pr-review/ReviewCoverage")({
|
|
20
|
-
status: Schema.Literals(["complete", "incomplete"]),
|
|
21
|
-
requiredPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
22
|
-
reviewedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
23
|
-
unreviewedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
24
|
-
failedUnits: Schema.Array(FailedReviewUnit).check(Schema.isMaxLength(8)),
|
|
25
|
-
reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(32))
|
|
26
|
-
}) {};
|
|
27
|
-
var ReviewInputCoverage = class extends Schema.Class("@effect-agent/pr-review/ReviewInputCoverage")({
|
|
28
|
-
status: Schema.Literals(["complete", "incomplete"]),
|
|
29
|
-
requiredPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
30
|
-
assignedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
31
|
-
/** Assigned paths whose model-visible diff was truncated by the evidence bound. */
|
|
32
|
-
partialPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
33
|
-
unassignedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
34
|
-
reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(20))
|
|
35
|
-
}) {};
|
|
36
|
-
var FailedReviewPass = class extends Schema.Class("@effect-agent/pr-review/FailedReviewPass")({
|
|
37
|
-
workId: Schema.NonEmptyString.check(Schema.isMaxLength(96)),
|
|
38
|
-
stage: Schema.Literals([
|
|
39
|
-
"discovery",
|
|
40
|
-
"specialist",
|
|
41
|
-
"verification"
|
|
42
|
-
]),
|
|
43
|
-
errorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256))
|
|
44
|
-
}) {};
|
|
45
|
-
var ReviewAssurance = class extends Schema.Class("@effect-agent/pr-review/ReviewAssurance")({
|
|
46
|
-
status: Schema.Literals([
|
|
47
|
-
"settled",
|
|
48
|
-
"incomplete",
|
|
49
|
-
"unverified"
|
|
50
|
-
]),
|
|
51
|
-
requiredGeneralDiscoveryPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
52
|
-
completedGeneralDiscoveryPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
53
|
-
requiredSpecialistPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
54
|
-
completedSpecialistPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
55
|
-
requiredVerificationPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
56
|
-
completedVerificationPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
57
|
-
discoveredCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
58
|
-
confirmedCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
59
|
-
rejectedCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
60
|
-
unsettledCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
61
|
-
/** Every failure remains visible within the coordinator's 32-call hard bound. */
|
|
62
|
-
failedPasses: Schema.Array(FailedReviewPass).check(Schema.isMaxLength(64)),
|
|
63
|
-
reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(32))
|
|
64
|
-
}) {};
|
|
65
|
-
const toolTrace = (events) => {
|
|
66
|
-
const declared = /* @__PURE__ */ new Map();
|
|
67
|
-
const succeeded = /* @__PURE__ */ new Map();
|
|
68
|
-
const failed = /* @__PURE__ */ new Map();
|
|
69
|
-
for (const event of events) {
|
|
70
|
-
if (event._tag === "ToolCallDeclared") declared.set(event.toolCallId, event);
|
|
71
|
-
if (event._tag === "ToolCallSucceeded") succeeded.set(event.toolCallId, event);
|
|
72
|
-
if (event._tag === "ToolCallFailed") failed.set(event.toolCallId, event);
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
declared,
|
|
76
|
-
succeeded,
|
|
77
|
-
failed
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
const sortedUnique = (values) => [...new Set(values)].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
81
|
-
const boundedListReason = (label, values) => {
|
|
82
|
-
const items = sortedUnique(values);
|
|
83
|
-
let rendered = `${label} (${items.length}): `;
|
|
84
|
-
for (let index = 0; index < items.length; index += 1) {
|
|
85
|
-
const item = items[index] ?? "";
|
|
86
|
-
const separator = index === 0 ? "" : ", ";
|
|
87
|
-
const omitted = items.length - index - 1;
|
|
88
|
-
const suffix = omitted === 0 ? "" : ` … (+${omitted} more)`;
|
|
89
|
-
if (`${rendered}${separator}${item}${suffix}`.length > 1e3) {
|
|
90
|
-
const omission = `… (+${items.length - index} more)`;
|
|
91
|
-
return `${rendered.slice(0, 1e3 - omission.length)}${omission}`;
|
|
92
|
-
}
|
|
93
|
-
rendered = `${rendered}${separator}${item}`;
|
|
94
|
-
}
|
|
95
|
-
return rendered;
|
|
96
|
-
};
|
|
97
|
-
const flatInputCoverage = (files, totalFiles, trace) => {
|
|
98
|
-
const requiredPaths = sortedUnique(files.map((file) => file.path));
|
|
99
|
-
const assigned = /* @__PURE__ */ new Set();
|
|
100
|
-
const partial = /* @__PURE__ */ new Set();
|
|
101
|
-
const failedPaths = /* @__PURE__ */ new Set();
|
|
102
|
-
for (const [toolCallId, declaration] of trace.declared) {
|
|
103
|
-
if (declaration.toolName !== "read_file_diff") continue;
|
|
104
|
-
const query = Schema.decodeUnknownOption(FileDiffQuery)(declaration.parameters);
|
|
105
|
-
if (Option.isNone(query)) continue;
|
|
106
|
-
const success = trace.succeeded.get(toolCallId);
|
|
107
|
-
if (success !== void 0) {
|
|
108
|
-
assigned.add(query.value.path);
|
|
109
|
-
const view = Schema.decodeUnknownOption(FileDiffView)(success.result);
|
|
110
|
-
if (Option.isSome(view) && view.value.truncated) partial.add(query.value.path);
|
|
111
|
-
}
|
|
112
|
-
if (trace.failed.has(toolCallId)) failedPaths.add(query.value.path);
|
|
113
|
-
}
|
|
114
|
-
const undiffable = files.filter((file) => !isReviewableFile(file)).map((file) => file.path);
|
|
115
|
-
const unassigned = requiredPaths.filter((path) => !assigned.has(path) || undiffable.includes(path) || failedPaths.has(path));
|
|
116
|
-
const reasons = [];
|
|
117
|
-
if (files.length < totalFiles) reasons.push(`review range exposed ${files.length} of ${totalFiles} required files`);
|
|
118
|
-
if (undiffable.length > 0) reasons.push(boundedListReason("required paths have no reviewable diff or bounded text", undiffable));
|
|
119
|
-
if (failedPaths.size > 0) reasons.push(boundedListReason("diff reads failed", failedPaths));
|
|
120
|
-
if (partial.size > 0) reasons.push(boundedListReason("model-visible diff evidence was truncated", partial));
|
|
121
|
-
if (unassigned.length > 0) reasons.push(boundedListReason("required paths received no successful diff input", unassigned));
|
|
122
|
-
return ReviewInputCoverage.make({
|
|
123
|
-
status: reasons.length === 0 ? "complete" : "incomplete",
|
|
124
|
-
requiredPaths,
|
|
125
|
-
assignedPaths: sortedUnique(assigned),
|
|
126
|
-
partialPaths: sortedUnique(partial),
|
|
127
|
-
unassignedPaths: sortedUnique(unassigned),
|
|
128
|
-
reasons
|
|
129
|
-
});
|
|
130
|
-
};
|
|
131
|
-
const fanOutInputCoverage = (files, totalFiles) => {
|
|
132
|
-
const plan = planReviewUnits(files, { totalChangedFiles: totalFiles });
|
|
133
|
-
const assignedPaths = sortedUnique(plan.units.flatMap((unit) => unit.paths));
|
|
134
|
-
const unassignedPaths = sortedUnique([...plan.undiffablePaths, ...plan.unassignedPaths]);
|
|
135
|
-
const reasons = [];
|
|
136
|
-
if (plan.truncated) reasons.push(`review range exposed ${files.length} of ${totalFiles} required files`);
|
|
137
|
-
if (plan.undiffablePaths.length > 0) reasons.push(boundedListReason("required paths have no reviewable diff or bounded text", plan.undiffablePaths));
|
|
138
|
-
if (plan.partialEvidencePaths.length > 0) reasons.push(boundedListReason("fan-out capacity left some deterministic evidence shards unassigned", plan.partialEvidencePaths));
|
|
139
|
-
if (plan.unassignedEvidenceShardCount > 0) {
|
|
140
|
-
reasons.push(`${plan.unassignedEvidenceShardCount} deterministic evidence shard(s) exceeded fan-out capacity`);
|
|
141
|
-
reasons.push(boundedListReason(`unassigned evidence shard identifier sample (${plan.unassignedEvidenceShardIds.length} of ${plan.unassignedEvidenceShardCount})`, plan.unassignedEvidenceShardIds));
|
|
142
|
-
}
|
|
143
|
-
if (plan.unassignedPaths.length > 0) reasons.push(boundedListReason("fan-out capacity left paths unassigned", plan.unassignedPaths));
|
|
144
|
-
return ReviewInputCoverage.make({
|
|
145
|
-
status: reasons.length === 0 ? "complete" : "incomplete",
|
|
146
|
-
requiredPaths: sortedUnique(files.map((file) => file.path)),
|
|
147
|
-
assignedPaths,
|
|
148
|
-
partialPaths: plan.partialEvidencePaths,
|
|
149
|
-
unassignedPaths,
|
|
150
|
-
reasons
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
const sameStrings = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
|
|
154
|
-
const candidateKey = (candidate) => JSON.stringify(Schema.encodeSync(ReviewCandidate)(candidate));
|
|
155
|
-
const sameCandidates = (left, right) => left.length === right.length && left.every((candidate, index) => {
|
|
156
|
-
const corresponding = right[index];
|
|
157
|
-
return corresponding !== void 0 && candidateKey(candidate) === candidateKey(corresponding);
|
|
158
|
-
});
|
|
159
|
-
const delegationDeclarations = (trace) => [...trace.declared].flatMap(([id, declaration]) => {
|
|
160
|
-
if (declaration.toolName !== "delegate_file_review") return [];
|
|
161
|
-
const request = Schema.decodeUnknownOption(FileReviewRequest)(declaration.parameters);
|
|
162
|
-
return Option.isNone(request) ? [] : [{
|
|
163
|
-
id,
|
|
164
|
-
request: request.value
|
|
165
|
-
}];
|
|
166
|
-
});
|
|
167
|
-
const failureTag = (trace, id) => {
|
|
168
|
-
const failed = trace.failed.get(id);
|
|
169
|
-
if (failed !== void 0) return failed.errorTag;
|
|
170
|
-
const succeeded = trace.succeeded.get(id);
|
|
171
|
-
if (succeeded === void 0) return void 0;
|
|
172
|
-
const returned = Schema.decodeUnknownOption(FileReviewDelegationFailure)(succeeded.result);
|
|
173
|
-
if (Option.isNone(returned)) return void 0;
|
|
174
|
-
return returned.value._tag === "FileReviewUnitFailed" ? `${returned.value._tag}:${returned.value.childErrorTag}` : returned.value._tag;
|
|
175
|
-
};
|
|
176
|
-
const exactDiscoveryRequest = (request, pass) => request.phase === "discovery" && request.workId === pass.passId && request.unitId === pass.unitId && request.perspective === pass.perspective && sameStrings(request.paths, pass.paths) && sameStrings(request.evidenceShardIds, pass.evidenceShardIds) && sameStrings(request.riskCategories, pass.riskCategories) && request.candidates.length === 0;
|
|
177
|
-
const validCandidate = (candidate, pass, unit, files, anchorFiles) => {
|
|
178
|
-
const allowed = new Set(pass.paths);
|
|
179
|
-
const kind = candidate._tag === "FindingCandidate" ? "finding" : "concern";
|
|
180
|
-
const idPrefix = `${pass.passId}:${kind}:`;
|
|
181
|
-
return candidate.candidateId.startsWith(idPrefix) && /^\d{3}$/.test(candidate.candidateId.slice(idPrefix.length)) && candidate.workId === pass.passId && candidate.unitId === pass.unitId && candidate.evidencePaths.length > 0 && candidate.evidencePaths.every((path) => allowed.has(path)) && (candidate._tag !== "FindingCandidate" || allowed.has(candidate.finding.path) && anchorViolation(candidate.finding, anchorFiles) === void 0 && findingAnchorInUnitEvidence(candidate.finding, unit, files));
|
|
182
|
-
};
|
|
183
|
-
const flatAssurance = () => ({
|
|
184
|
-
assurance: ReviewAssurance.make({
|
|
185
|
-
status: "unverified",
|
|
186
|
-
requiredGeneralDiscoveryPasses: 1,
|
|
187
|
-
completedGeneralDiscoveryPasses: 1,
|
|
188
|
-
requiredSpecialistPasses: 0,
|
|
189
|
-
completedSpecialistPasses: 0,
|
|
190
|
-
requiredVerificationPasses: 1,
|
|
191
|
-
completedVerificationPasses: 0,
|
|
192
|
-
discoveredCandidates: 0,
|
|
193
|
-
confirmedCandidates: 0,
|
|
194
|
-
rejectedCandidates: 0,
|
|
195
|
-
unsettledCandidates: 0,
|
|
196
|
-
failedPasses: [],
|
|
197
|
-
reasons: ["flat review has no independent candidate-verification pass; use the fan-out pipeline for a settled assurance result"]
|
|
198
|
-
}),
|
|
199
|
-
confirmedFindings: [],
|
|
200
|
-
confirmedConcerns: [],
|
|
201
|
-
walkthrough: []
|
|
202
|
-
});
|
|
203
|
-
const fanOutAssurance = (files, totalFiles, anchorFiles, trace) => {
|
|
204
|
-
const plan = planReviewUnits(files, { totalChangedFiles: totalFiles });
|
|
205
|
-
const declarations = delegationDeclarations(trace);
|
|
206
|
-
const consumedDeclarationIds = /* @__PURE__ */ new Set();
|
|
207
|
-
const failedPasses = [];
|
|
208
|
-
const reasons = [];
|
|
209
|
-
const candidatesByUnit = /* @__PURE__ */ new Map();
|
|
210
|
-
const walkthrough = [];
|
|
211
|
-
let completedGeneralDiscoveryPasses = 0;
|
|
212
|
-
let completedSpecialistPasses = 0;
|
|
213
|
-
for (const pass of plan.discoveryPasses) {
|
|
214
|
-
const unit = plan.units.find((candidate) => candidate.unitId === pass.unitId);
|
|
215
|
-
const matching = declarations.filter(({ request }) => exactDiscoveryRequest(request, pass));
|
|
216
|
-
const stage = pass.perspective === "risk-specialist" ? "specialist" : "discovery";
|
|
217
|
-
if (matching.length !== 1) {
|
|
218
|
-
failedPasses.push(FailedReviewPass.make({
|
|
219
|
-
workId: pass.passId,
|
|
220
|
-
stage,
|
|
221
|
-
errorTag: matching.length === 0 ? "PassNotAssigned" : "PassAssignedMultipleTimes"
|
|
222
|
-
}));
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const call = matching[0];
|
|
226
|
-
if (call === void 0) {
|
|
227
|
-
failedPasses.push(FailedReviewPass.make({
|
|
228
|
-
workId: pass.passId,
|
|
229
|
-
stage,
|
|
230
|
-
errorTag: "PassLookupInvariantFailed"
|
|
231
|
-
}));
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
consumedDeclarationIds.add(call.id);
|
|
235
|
-
const failure = failureTag(trace, call.id);
|
|
236
|
-
const succeeded = trace.succeeded.get(call.id);
|
|
237
|
-
const result = succeeded === void 0 ? Option.none() : Schema.decodeUnknownOption(FileReviewUnitResult)(succeeded.result);
|
|
238
|
-
if (failure !== void 0 || Option.isNone(result) || result.value.phase !== "discovery" || result.value.workId !== pass.passId || result.value.unitId !== pass.unitId || result.value.assessments.length !== 0) {
|
|
239
|
-
failedPasses.push(FailedReviewPass.make({
|
|
240
|
-
workId: pass.passId,
|
|
241
|
-
stage,
|
|
242
|
-
errorTag: failure ?? "DiscoveryDidNotSettleExactly"
|
|
243
|
-
}));
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const ids = /* @__PURE__ */ new Set();
|
|
247
|
-
let candidatesValid = true;
|
|
248
|
-
for (const candidate of result.value.candidates) {
|
|
249
|
-
if (unit === void 0 || ids.has(candidate.candidateId) || !validCandidate(candidate, pass, unit, files, anchorFiles)) {
|
|
250
|
-
candidatesValid = false;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
ids.add(candidate.candidateId);
|
|
254
|
-
}
|
|
255
|
-
const unitCandidates = candidatesByUnit.get(pass.unitId) ?? [];
|
|
256
|
-
const unitIds = new Set(unitCandidates.map((candidate) => candidate.candidateId));
|
|
257
|
-
if (result.value.candidates.some((candidate) => unitIds.has(candidate.candidateId))) candidatesValid = false;
|
|
258
|
-
if (!candidatesValid) {
|
|
259
|
-
failedPasses.push(FailedReviewPass.make({
|
|
260
|
-
workId: pass.passId,
|
|
261
|
-
stage,
|
|
262
|
-
errorTag: "DiscoveryCandidateMismatch"
|
|
263
|
-
}));
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
if (stage === "specialist") completedSpecialistPasses += 1;
|
|
267
|
-
else completedGeneralDiscoveryPasses += 1;
|
|
268
|
-
const subjectKeys = new Set(unitCandidates.map(reviewCandidateSubjectKey));
|
|
269
|
-
for (const candidate of result.value.candidates) {
|
|
270
|
-
const subjectKey = reviewCandidateSubjectKey(candidate);
|
|
271
|
-
if (subjectKeys.has(subjectKey)) continue;
|
|
272
|
-
subjectKeys.add(subjectKey);
|
|
273
|
-
unitCandidates.push(candidate);
|
|
274
|
-
}
|
|
275
|
-
candidatesByUnit.set(pass.unitId, unitCandidates);
|
|
276
|
-
if (pass.perspective === "general") {
|
|
277
|
-
const allowed = new Set(pass.paths);
|
|
278
|
-
walkthrough.push(...result.value.fileSummaries.filter((entry) => allowed.has(entry.path)));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const confirmedCandidates = [];
|
|
282
|
-
let rejectedCandidates = 0;
|
|
283
|
-
let unsettledCandidates = 0;
|
|
284
|
-
let requiredVerificationPasses = 0;
|
|
285
|
-
let completedVerificationPasses = 0;
|
|
286
|
-
for (const unit of plan.units) {
|
|
287
|
-
const candidates = candidatesByUnit.get(unit.unitId) ?? [];
|
|
288
|
-
if (candidates.length === 0) continue;
|
|
289
|
-
requiredVerificationPasses += 1;
|
|
290
|
-
const workId = `${unit.unitId}-verification`;
|
|
291
|
-
const matching = declarations.filter(({ request }) => request.phase === "verification" && request.workId === workId && request.unitId === unit.unitId && request.perspective === "candidate-verification" && sameStrings(request.paths, unit.paths) && sameStrings(request.evidenceShardIds, unit.evidenceShards.map((shard) => shard.shardId)) && sameStrings(request.riskCategories, unit.riskCategories) && sameCandidates(request.candidates, candidates));
|
|
292
|
-
if (matching.length !== 1) {
|
|
293
|
-
unsettledCandidates += candidates.length;
|
|
294
|
-
failedPasses.push(FailedReviewPass.make({
|
|
295
|
-
workId,
|
|
296
|
-
stage: "verification",
|
|
297
|
-
errorTag: matching.length === 0 ? "VerificationNotAssignedOrCandidateMismatch" : "VerificationAssignedMultipleTimes"
|
|
298
|
-
}));
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
const call = matching[0];
|
|
302
|
-
if (call === void 0) {
|
|
303
|
-
unsettledCandidates += candidates.length;
|
|
304
|
-
failedPasses.push(FailedReviewPass.make({
|
|
305
|
-
workId,
|
|
306
|
-
stage: "verification",
|
|
307
|
-
errorTag: "PassLookupInvariantFailed"
|
|
308
|
-
}));
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
consumedDeclarationIds.add(call.id);
|
|
312
|
-
const failure = failureTag(trace, call.id);
|
|
313
|
-
const succeeded = trace.succeeded.get(call.id);
|
|
314
|
-
const result = succeeded === void 0 ? Option.none() : Schema.decodeUnknownOption(FileReviewUnitResult)(succeeded.result);
|
|
315
|
-
if (failure !== void 0 || Option.isNone(result) || result.value.phase !== "verification" || result.value.workId !== workId || result.value.unitId !== unit.unitId || result.value.candidates.length !== 0 || result.value.fileSummaries.length !== 0) {
|
|
316
|
-
unsettledCandidates += candidates.length;
|
|
317
|
-
failedPasses.push(FailedReviewPass.make({
|
|
318
|
-
workId,
|
|
319
|
-
stage: "verification",
|
|
320
|
-
errorTag: failure ?? "VerificationDidNotSettleExactly"
|
|
321
|
-
}));
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
const byId = new Map(candidates.map((candidate) => [candidate.candidateId, candidate]));
|
|
325
|
-
const assessedIds = /* @__PURE__ */ new Set();
|
|
326
|
-
let suggestionSettlementExact = true;
|
|
327
|
-
const exactAssessments = result.value.assessments.every((assessment) => {
|
|
328
|
-
const candidate = byId.get(assessment.candidateId);
|
|
329
|
-
if (candidate === void 0 || assessedIds.has(assessment.candidateId)) return false;
|
|
330
|
-
assessedIds.add(assessment.candidateId);
|
|
331
|
-
if (!assessmentSettlesSuggestionExactly(assessment, candidate)) suggestionSettlementExact = false;
|
|
332
|
-
return true;
|
|
333
|
-
});
|
|
334
|
-
if (byId.size !== candidates.length || !exactAssessments || assessedIds.size !== byId.size || !suggestionSettlementExact) {
|
|
335
|
-
unsettledCandidates += candidates.length;
|
|
336
|
-
failedPasses.push(FailedReviewPass.make({
|
|
337
|
-
workId,
|
|
338
|
-
stage: "verification",
|
|
339
|
-
errorTag: exactAssessments && !suggestionSettlementExact ? "SuggestionSettlementMismatch" : "VerificationAssessmentMismatch"
|
|
340
|
-
}));
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
completedVerificationPasses += 1;
|
|
344
|
-
for (const assessment of result.value.assessments) if (assessment.disposition === "confirmed") {
|
|
345
|
-
const candidate = byId.get(assessment.candidateId);
|
|
346
|
-
if (candidate !== void 0) confirmedCandidates.push({
|
|
347
|
-
assessment,
|
|
348
|
-
candidate
|
|
349
|
-
});
|
|
350
|
-
} else rejectedCandidates += 1;
|
|
351
|
-
}
|
|
352
|
-
const unexpected = [...trace.declared].filter(([id, declaration]) => declaration.toolName === "delegate_file_review" && !consumedDeclarationIds.has(id));
|
|
353
|
-
for (const [, declaration] of unexpected) {
|
|
354
|
-
const request = Schema.decodeUnknownOption(FileReviewRequest)(declaration.parameters);
|
|
355
|
-
failedPasses.push(FailedReviewPass.make({
|
|
356
|
-
workId: Option.isSome(request) ? request.value.workId : "invalid-delegation-request",
|
|
357
|
-
stage: Option.isSome(request) && request.value.phase === "verification" ? "verification" : "discovery",
|
|
358
|
-
errorTag: "UnexpectedPass"
|
|
359
|
-
}));
|
|
360
|
-
}
|
|
361
|
-
if (failedPasses.length > 0) reasons.push(boundedListReason("configured review passes did not settle", failedPasses.map((pass) => `${pass.workId} (${pass.errorTag})`)));
|
|
362
|
-
if (unsettledCandidates > 0) reasons.push(`${unsettledCandidates} discovered candidate(s) did not receive exact verification`);
|
|
363
|
-
const requiredSpecialistPasses = plan.discoveryPasses.filter((pass) => pass.perspective === "risk-specialist").length;
|
|
364
|
-
const requiredGeneralDiscoveryPasses = plan.discoveryPasses.length - requiredSpecialistPasses;
|
|
365
|
-
const discoveredCandidates = [...candidatesByUnit.values()].reduce((total, candidates) => total + candidates.length, 0);
|
|
366
|
-
return {
|
|
367
|
-
assurance: ReviewAssurance.make({
|
|
368
|
-
status: reasons.length === 0 ? "settled" : "incomplete",
|
|
369
|
-
requiredGeneralDiscoveryPasses,
|
|
370
|
-
completedGeneralDiscoveryPasses,
|
|
371
|
-
requiredSpecialistPasses,
|
|
372
|
-
completedSpecialistPasses,
|
|
373
|
-
requiredVerificationPasses,
|
|
374
|
-
completedVerificationPasses,
|
|
375
|
-
discoveredCandidates,
|
|
376
|
-
confirmedCandidates: confirmedCandidates.length,
|
|
377
|
-
rejectedCandidates,
|
|
378
|
-
unsettledCandidates,
|
|
379
|
-
failedPasses,
|
|
380
|
-
reasons
|
|
381
|
-
}),
|
|
382
|
-
confirmedFindings: confirmedCandidates.flatMap(({ assessment, candidate }) => candidate._tag === "FindingCandidate" ? [confirmedFindingForPublication(assessment, candidate)] : []),
|
|
383
|
-
confirmedConcerns: confirmedCandidates.flatMap(({ candidate }) => candidate._tag === "ConcernCandidate" ? [candidate.concern] : []),
|
|
384
|
-
walkthrough
|
|
385
|
-
};
|
|
386
|
-
};
|
|
387
|
-
const compatibilityCoverage = (inputCoverage, assurance) => {
|
|
388
|
-
const assuranceIncomplete = assurance.status === "incomplete";
|
|
389
|
-
const failedUnits = /* @__PURE__ */ new Map();
|
|
390
|
-
for (const pass of assurance.failedPasses) {
|
|
391
|
-
const unitId = pass.workId.slice(0, 8);
|
|
392
|
-
if (!failedUnits.has(unitId)) failedUnits.set(unitId, FailedReviewUnit.make({
|
|
393
|
-
unitId,
|
|
394
|
-
errorTag: `${pass.stage}:${pass.errorTag}`
|
|
395
|
-
}));
|
|
396
|
-
}
|
|
397
|
-
return ReviewCoverage.make({
|
|
398
|
-
status: inputCoverage.status === "complete" && !assuranceIncomplete ? "complete" : "incomplete",
|
|
399
|
-
requiredPaths: inputCoverage.requiredPaths,
|
|
400
|
-
reviewedPaths: inputCoverage.assignedPaths,
|
|
401
|
-
unreviewedPaths: sortedUnique([...inputCoverage.partialPaths, ...inputCoverage.unassignedPaths]),
|
|
402
|
-
failedUnits: [...failedUnits.values()].slice(0, 8),
|
|
403
|
-
reasons: [...inputCoverage.reasons, ...assuranceIncomplete ? assurance.reasons : []]
|
|
404
|
-
});
|
|
405
|
-
};
|
|
406
|
-
/** Assess one settled run without trusting coordinator prose or findings. */
|
|
407
|
-
const assessReviewPipeline = (input) => {
|
|
408
|
-
const trace = toolTrace(input.events);
|
|
409
|
-
let inputCoverage = input.shape === "fan-out" ? fanOutInputCoverage(input.files, input.totalFiles) : flatInputCoverage(input.files, input.totalFiles, trace);
|
|
410
|
-
if (input.anchorFiles.length < input.totalAnchorFiles) inputCoverage = ReviewInputCoverage.make({
|
|
411
|
-
...inputCoverage,
|
|
412
|
-
status: "incomplete",
|
|
413
|
-
reasons: [...inputCoverage.reasons, `full pull-request anchor surface exposed ${input.anchorFiles.length} of ${input.totalAnchorFiles} required files`]
|
|
414
|
-
});
|
|
415
|
-
const assessed = input.shape === "fan-out" ? fanOutAssurance(input.files, input.totalFiles, input.anchorFiles, trace) : flatAssurance();
|
|
416
|
-
return {
|
|
417
|
-
inputCoverage,
|
|
418
|
-
assurance: assessed.assurance,
|
|
419
|
-
coverage: compatibilityCoverage(inputCoverage, assessed.assurance),
|
|
420
|
-
confirmedFindings: assessed.confirmedFindings,
|
|
421
|
-
confirmedConcerns: assessed.confirmedConcerns,
|
|
422
|
-
walkthrough: assessed.walkthrough
|
|
423
|
-
};
|
|
424
|
-
};
|
|
425
|
-
/** Compatibility helper; prefer assessReviewPipeline for precise claims. */
|
|
426
|
-
const assessReviewCoverage = (input) => assessReviewPipeline(input).coverage;
|
|
427
|
-
/** Host-verified summaries from successful general discovery passes only. */
|
|
428
|
-
const collectUnitFileSummaries = (events) => {
|
|
429
|
-
const trace = toolTrace(events);
|
|
430
|
-
return delegationDeclarations(trace).flatMap(({ id, request }) => {
|
|
431
|
-
if (request.phase !== "discovery" || request.perspective !== "general") return [];
|
|
432
|
-
const success = trace.succeeded.get(id);
|
|
433
|
-
if (success === void 0 || trace.failed.has(id)) return [];
|
|
434
|
-
const result = Schema.decodeUnknownOption(FileReviewUnitResult)(success.result);
|
|
435
|
-
if (Option.isNone(result) || result.value.phase !== "discovery" || result.value.workId !== request.workId || result.value.unitId !== request.unitId) return [];
|
|
436
|
-
const assigned = new Set(request.paths);
|
|
437
|
-
return result.value.fileSummaries.filter((entry) => assigned.has(entry.path));
|
|
438
|
-
});
|
|
439
|
-
};
|
|
440
|
-
//#endregion
|
|
441
8
|
//#region src/internal/effort.ts
|
|
442
9
|
/**
|
|
443
10
|
* Names accepted on user-facing surfaces (the action input, the CLI flag),
|
|
@@ -566,7 +133,7 @@ const severityEmoji = {
|
|
|
566
133
|
important: "⚠️",
|
|
567
134
|
nit: "💅"
|
|
568
135
|
};
|
|
569
|
-
const severityRank
|
|
136
|
+
const severityRank = {
|
|
570
137
|
blocking: 0,
|
|
571
138
|
important: 1,
|
|
572
139
|
nit: 2
|
|
@@ -666,9 +233,9 @@ const severityCounts = (review, carriedFindings = [], carriedConcerns = []) => {
|
|
|
666
233
|
*/
|
|
667
234
|
const renderVerdictCallout = (review, options) => {
|
|
668
235
|
const counts = severityCounts(review, options.carriedFindings, options.carriedConcerns);
|
|
669
|
-
if (options.inputCoverage?.status === "incomplete" || options.inputCoverage === void 0 && options.coverage?.status === "incomplete") return `> [!CAUTION]\n> Input coverage is incomplete — the check must not pass.${counts.blocking > 0 ? ` It also has ${countNoun(counts.blocking, "blocking finding")}.` : ""}`;
|
|
670
|
-
if (options.assurance !== void 0 && options.assurance.status !== "settled") return `> [!CAUTION]\n> Configured review assurance did not settle — the check must not pass.${counts.blocking > 0 ? ` It also has ${countNoun(counts.blocking, "blocking finding")}.` : ""}`;
|
|
671
236
|
if (counts.blocking > 0) return `> [!CAUTION]\n> ${countNoun(counts.blocking, "blocking finding")} — do not merge before addressing ${counts.blocking === 1 ? "it" : "them"}.`;
|
|
237
|
+
const carried = options.unreviewedPaths?.length ?? 0;
|
|
238
|
+
if (options.inputCoverage?.status === "incomplete" || options.assurance?.status === "incomplete") return `> [!WARNING]\n> Review infrastructure did not settle — a reviewer-side gap, NOT a request to change code.${carried > 0 ? ` ${countNoun(carried, "affected path")} ${carried === 1 ? "is" : "are"} carried forward and retried automatically on the next run.` : ""} The check reports "incomplete" until a run settles.`;
|
|
672
239
|
if (counts.important > 0) return `> [!IMPORTANT]\n> ${countNoun(counts.important, "important finding")} to address before merging.`;
|
|
673
240
|
if (counts.total > 0) return "> ℹ️ Minor suggestions only — mergeable as-is.";
|
|
674
241
|
return review.verdict === "approve" ? "> ✅ No issues found." : "> ℹ️ No findings — see the summary.";
|
|
@@ -785,14 +352,11 @@ const planPublication = (review, files, options) => {
|
|
|
785
352
|
});
|
|
786
353
|
}
|
|
787
354
|
const walkthrough = planWalkthrough(review.walkthrough, files);
|
|
788
|
-
const sortedConcerns = [...review.concerns ?? []].sort((a, b) => severityRank
|
|
789
|
-
const sortedDemoted = [...demoted].sort((a, b) => severityRank
|
|
355
|
+
const sortedConcerns = [...review.concerns ?? []].sort((a, b) => severityRank[a.severity] - severityRank[b.severity]);
|
|
356
|
+
const sortedDemoted = [...demoted].sort((a, b) => severityRank[a.finding.severity] - severityRank[b.finding.severity]);
|
|
790
357
|
const footerParts = ["Automated review by @effect-agent/pr-review"];
|
|
791
358
|
if (options.modelLabel !== void 0) footerParts.push(options.modelLabel);
|
|
792
|
-
if (options.usage !== void 0
|
|
793
|
-
const scope = options.usageScope === "coordinator" ? " (coordinator)" : "";
|
|
794
|
-
footerParts.push(`${options.usage.inputTokens} in / ${options.usage.outputTokens} out tokens${scope}`);
|
|
795
|
-
}
|
|
359
|
+
if (options.usage !== void 0) footerParts.push(`${options.usage.inputTokens} in / ${options.usage.outputTokens} out tokens`);
|
|
796
360
|
if (options.runUrl !== void 0) footerParts.push(`[run](${options.runUrl})`);
|
|
797
361
|
footerParts.push(`reviewed at ${options.headSha.slice(0, 7)}`);
|
|
798
362
|
const footer = `_${footerParts.join(" · ")}._`;
|
|
@@ -810,20 +374,19 @@ const planPublication = (review, files, options) => {
|
|
|
810
374
|
const parts = [renderVerdictCallout(review, {
|
|
811
375
|
carriedFindings,
|
|
812
376
|
carriedConcerns,
|
|
813
|
-
coverage: options.coverage,
|
|
814
377
|
inputCoverage: options.inputCoverage,
|
|
815
|
-
assurance: options.assurance
|
|
378
|
+
assurance: options.assurance,
|
|
379
|
+
unreviewedPaths: options.unreviewedPaths
|
|
816
380
|
})];
|
|
817
381
|
if (options.reviewMode !== void 0 && options.reviewReason !== void 0) parts.push("", options.reviewMode === "incremental" ? `**Incremental scope:** reopened ${options.reviewFilesVisible ?? files.length} affected file(s) ${options.reviewReason}. Unchanged settled scope was preserved and not reopened.` : `**Full-diff scope:** ${options.reviewReason}.`);
|
|
818
382
|
if (options.stateNotice !== void 0) parts.push("", `⚠️ Continuity state was not stored (${options.stateNotice.slice(0, 1e3)}); the next run will safely review the full diff.`);
|
|
819
383
|
parts.push("", renderReviewStats(files, options.totalChangedFiles, counts));
|
|
820
|
-
if (options.inputCoverage !== void 0 && options.assurance !== void 0) parts.push("", `**Input coverage:** ${options.inputCoverage.status} (${options.inputCoverage.assignedPaths.length}/${options.inputCoverage.requiredPaths.length} paths assigned, ${options.inputCoverage.partialPaths.length} partial) · **Review assurance:** ${options.assurance.status} (${options.assurance.completedGeneralDiscoveryPasses}/${options.assurance.requiredGeneralDiscoveryPasses} general discovery, ${options.assurance.completedSpecialistPasses}/${options.assurance.requiredSpecialistPasses} specialist, ${options.assurance.completedVerificationPasses}/${options.assurance.requiredVerificationPasses} verification; ${options.assurance.confirmedCandidates} confirmed / ${options.assurance.rejectedCandidates} rejected / ${options.assurance.unsettledCandidates} unsettled candidates)`);
|
|
384
|
+
if (options.inputCoverage !== void 0 && options.assurance !== void 0) parts.push("", `**Input coverage:** ${options.inputCoverage.status} (${options.inputCoverage.assignedPaths.length}/${options.inputCoverage.requiredPaths.length} paths assigned, ${options.inputCoverage.partialPaths.length} partial) · **Review assurance:** ${options.assurance.status} (${options.assurance.completedGeneralDiscoveryPasses}/${options.assurance.requiredGeneralDiscoveryPasses} general discovery, ${options.assurance.completedSpecialistPasses}/${options.assurance.requiredSpecialistPasses} specialist, ${options.assurance.completedVerificationPasses}/${options.assurance.requiredVerificationPasses} verification; ${options.assurance.confirmedCandidates} confirmed / ${options.assurance.rejectedCandidates} rejected / ${options.assurance.unsettledCandidates} unsettled${options.assurance.discardedInvalidFindings > 0 ? ` / ${options.assurance.discardedInvalidFindings} discarded` : ""} candidates)`);
|
|
821
385
|
parts.push("", review.summary);
|
|
822
386
|
if (walkthroughKept && walkthrough.length > 0) parts.push("", renderWalkthrough(walkthrough));
|
|
823
387
|
else if (walkthrough.length > 0) parts.push("", "⚠️ Walkthrough omitted — the body exceeded GitHub's review size cap.");
|
|
824
|
-
if (options.inputCoverage?.status === "incomplete") parts.push("", "###
|
|
825
|
-
|
|
826
|
-
if (options.assurance !== void 0 && options.assurance.status !== "settled") parts.push("", "### 🛑 Incomplete review assurance", "", ...options.assurance.reasons.map((reason) => `- ${reason}`));
|
|
388
|
+
if (options.inputCoverage?.status === "incomplete") parts.push("", "### ⚠️ Incomplete input coverage", "", ...options.inputCoverage.reasons.map((reason) => `- ${reason}`));
|
|
389
|
+
if (options.assurance?.status === "incomplete") parts.push("", "### ⚠️ Unsettled review passes", "", "The passes below failed on the reviewer's side after a bounded retry. Their paths are carried forward and re-reviewed automatically on the next run — do not change code to satisfy this section.", "", ...options.assurance.reasons.map((reason) => `- ${reason}`));
|
|
827
390
|
if (carriedFindings.length > 0) parts.push("", "<details>", `<summary>Unresolved findings carried from unchanged scope (${carriedFindings.length})</summary>`, "", ...carriedFindings.map(renderCarriedFinding), "", "</details>");
|
|
828
391
|
if (carriedConcerns.length > 0) {
|
|
829
392
|
parts.push("", "### Unresolved concerns carried to the final audit");
|
|
@@ -838,7 +401,8 @@ const planPublication = (review, files, options) => {
|
|
|
838
401
|
return parts.join("\n");
|
|
839
402
|
};
|
|
840
403
|
const counts = severityCounts(review, options.carriedFindings ?? [], options.carriedConcerns ?? []);
|
|
841
|
-
const
|
|
404
|
+
const unclean = options.inputCoverage?.status === "incomplete" || options.assurance?.status === "incomplete";
|
|
405
|
+
const event = !options.applyVerdict ? "COMMENT" : counts.blocking > 0 ? "REQUEST_CHANGES" : review.verdict === "approve" && counts.important === 0 && !unclean ? "APPROVE" : "COMMENT";
|
|
842
406
|
const tail = [
|
|
843
407
|
renderReviewMetadata({
|
|
844
408
|
headSha: options.headSha,
|
|
@@ -895,12 +459,9 @@ const reviewBudgetLimits = UsageBudgetLimits.make({
|
|
|
895
459
|
maxDurationMillis: 48e4
|
|
896
460
|
});
|
|
897
461
|
/**
|
|
898
|
-
* Run-level bounds for the fan-out
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
* own `AgentPolicy`, never silently by the parent's budget. The duration
|
|
902
|
-
* ceiling is wider because delegation Tool Calls hold the parent turn open
|
|
903
|
-
* while bounded children run.
|
|
462
|
+
* Run-level bounds for the fan-out pipeline. One budget observes EVERY child
|
|
463
|
+
* pass, so the ceiling covers bounded parallel discovery and verification
|
|
464
|
+
* plus the one-retry allowance.
|
|
904
465
|
*/
|
|
905
466
|
const fanOutReviewBudgetLimits = UsageBudgetLimits.make({
|
|
906
467
|
maxInputTokens: 6e5,
|
|
@@ -920,23 +481,16 @@ var ReviewRunOutcome = class extends Schema.Class("@effect-agent/pr-review/Revie
|
|
|
920
481
|
coverage: ReviewCoverage,
|
|
921
482
|
/** Exact path/evidence assignment, distinct from semantic review work. */
|
|
922
483
|
inputCoverage: ReviewInputCoverage,
|
|
923
|
-
/** Settlement of
|
|
484
|
+
/** Settlement of scheduled discovery, specialist, and verification work. */
|
|
924
485
|
assurance: ReviewAssurance,
|
|
486
|
+
/** Retryable scope this run could not settle; carried to the next run. */
|
|
487
|
+
unreviewedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
|
|
925
488
|
plan: ReviewPublicationPlan,
|
|
926
489
|
published: Schema.optionalKey(PublishedReview),
|
|
927
|
-
turns
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
* the COORDINATOR only — delegated children are bounded and accounted
|
|
931
|
-
* separately by their reservations.
|
|
932
|
-
*/
|
|
490
|
+
/** Total settled model turns (all child passes for the fan-out pipeline). */
|
|
491
|
+
turns: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
492
|
+
/** The run budget's observed usage across the whole run. */
|
|
933
493
|
usage: Schema.optionalKey(UsageTotals),
|
|
934
|
-
/**
|
|
935
|
-
* What `usage` observed: the whole run, or a fan-out coordinator only.
|
|
936
|
-
* Absent when the caller declared no scope — consumers must not present
|
|
937
|
-
* unscoped usage as whole-run totals.
|
|
938
|
-
*/
|
|
939
|
-
usageScope: Schema.optionalKey(Schema.Literals(["run", "coordinator"])),
|
|
940
494
|
reviewMode: Schema.optionalKey(Schema.Literals(["incremental", "full"])),
|
|
941
495
|
reviewReason: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1e3))),
|
|
942
496
|
state: Schema.optionalKey(ReviewState)
|
|
@@ -960,63 +514,20 @@ const enforceFindingsBound = (review, maxFindings) => review.findings.length <=
|
|
|
960
514
|
...review.walkthrough !== void 0 ? { walkthrough: review.walkthrough } : {}
|
|
961
515
|
});
|
|
962
516
|
const findingKey = (finding) => `${finding.path}\u0000${finding.startLine}\u0000${finding.endLine}\u0000${finding.severity}\u0000${finding.title}`;
|
|
963
|
-
const severityRank = {
|
|
964
|
-
blocking: 0,
|
|
965
|
-
important: 1,
|
|
966
|
-
nit: 2
|
|
967
|
-
};
|
|
968
|
-
const rankAndDedupeConcerns = (concerns) => {
|
|
969
|
-
const byContent = /* @__PURE__ */ new Map();
|
|
970
|
-
for (const concern of concerns) {
|
|
971
|
-
const key = `${concern.title}\u0000${concern.body}`;
|
|
972
|
-
const previous = byContent.get(key);
|
|
973
|
-
if (previous === void 0 || severityRank[concern.severity] < severityRank[previous.severity]) byContent.set(key, concern);
|
|
974
|
-
}
|
|
975
|
-
return [...byContent.values()].sort((left, right) => severityRank[left.severity] - severityRank[right.severity]).slice(0, 10);
|
|
976
|
-
};
|
|
977
517
|
/**
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* Layer's requirements stay visible in this Effect's `R`.
|
|
518
|
+
* The shared settlement tail: carry unchanged prior scope, decide whether
|
|
519
|
+
* this run's continuity state can be signed, plan the exact publication, and
|
|
520
|
+
* (optionally) post it. Continuity requires only that the run COMPLETED with
|
|
521
|
+
* a trustworthy full-surface fingerprint — never that every pass settled;
|
|
522
|
+
* unsettled scope travels inside the state instead of freezing it.
|
|
984
523
|
*/
|
|
985
|
-
const
|
|
986
|
-
const
|
|
987
|
-
const metadata = yield* source.metadata;
|
|
988
|
-
const files = yield* source.changedFiles;
|
|
989
|
-
const anchorFiles = yield* source.anchorFiles;
|
|
524
|
+
const settleReviewRun = (core, context, options) => Effect.gen(function* () {
|
|
525
|
+
const { metadata, files, anchorFiles, fingerprint, usage } = context;
|
|
990
526
|
const executionContext = Option.getOrUndefined(yield* Effect.serviceOption(ReviewExecutionContext));
|
|
991
|
-
const
|
|
992
|
-
const
|
|
993
|
-
const
|
|
994
|
-
const budget = yield* makeUsageBudget(options.limits ?? reviewBudgetLimits);
|
|
995
|
-
const detached = yield* AgentRuntime.start(binding, mission, {
|
|
996
|
-
budget: toRunBudgetHook(budget),
|
|
997
|
-
estimateCostMicrousd: () => Effect.succeed(500)
|
|
998
|
-
});
|
|
999
|
-
const result = yield* detached.await;
|
|
1000
|
-
const events = yield* detached.events;
|
|
1001
|
-
const decoded = yield* Schema.decodeUnknownEffect(CodeReview)(result.output);
|
|
527
|
+
const review = enforceFindingsBound(core.review, clampMaxFindings(options.maxFindings));
|
|
528
|
+
const { inputCoverage, assurance } = core;
|
|
529
|
+
const unreviewedPaths = [...new Set(core.unreviewedPaths)].sort();
|
|
1002
530
|
const reviewTotalFiles = executionContext?.totalFiles ?? metadata.totalChangedFiles;
|
|
1003
|
-
const pipeline = assessReviewPipeline({
|
|
1004
|
-
shape: options.reviewShape ?? "flat",
|
|
1005
|
-
files,
|
|
1006
|
-
totalFiles: reviewTotalFiles,
|
|
1007
|
-
anchorFiles,
|
|
1008
|
-
totalAnchorFiles: metadata.totalChangedFiles,
|
|
1009
|
-
events
|
|
1010
|
-
});
|
|
1011
|
-
const verifiedReview = options.reviewShape !== "fan-out" ? decoded : CodeReview.make({
|
|
1012
|
-
summary: decoded.summary,
|
|
1013
|
-
verdict: decoded.verdict,
|
|
1014
|
-
findings: rankAndDedupeFindings(pipeline.confirmedFindings),
|
|
1015
|
-
...pipeline.confirmedConcerns.length === 0 ? {} : { concerns: rankAndDedupeConcerns(pipeline.confirmedConcerns) },
|
|
1016
|
-
...pipeline.walkthrough.length === 0 ? {} : { walkthrough: pipeline.walkthrough }
|
|
1017
|
-
});
|
|
1018
|
-
const review = enforceFindingsBound(verifiedReview, clampMaxFindings(options.maxFindings));
|
|
1019
|
-
const usage = yield* budget.snapshot;
|
|
1020
531
|
const affectedPaths = new Set(executionContext?.affectedPaths ?? files.flatMap((file) => file.previousPath === void 0 ? [file.path] : [file.path, file.previousPath]));
|
|
1021
532
|
const priorState = executionContext?.mode === "incremental" ? executionContext.priorState : void 0;
|
|
1022
533
|
const carriedCandidates = priorState?.unresolvedFindings.filter((finding) => !affectedPaths.has(finding.path)).map(fromStoredFinding) ?? [];
|
|
@@ -1032,9 +543,11 @@ const executeReview = (binding, options) => Effect.gen(function* () {
|
|
|
1032
543
|
const key = `${concern.title}\u0000${concern.body}`;
|
|
1033
544
|
return activeConcernKeys.has(key) && !currentConcernKeys.has(key);
|
|
1034
545
|
});
|
|
1035
|
-
const
|
|
1036
|
-
const
|
|
1037
|
-
|
|
546
|
+
const settled = inputCoverage.status === "complete" && assurance.status !== "incomplete" && unreviewedPaths.length === 0;
|
|
547
|
+
const skipFingerprint = settled ? fingerprint : void 0;
|
|
548
|
+
const carriedScopeFits = unreviewedPaths.length <= 100;
|
|
549
|
+
const stateCandidate = executionContext !== void 0 && fingerprint !== void 0 && metadata.baseSha !== void 0 && anchorFiles.length >= metadata.totalChangedFiles && carriedScopeFits && executionContext.stateAuthenticator?.status === "available" ? ReviewState.make({
|
|
550
|
+
version: 2,
|
|
1038
551
|
repository: metadata.repository,
|
|
1039
552
|
pullRequestNumber: metadata.number,
|
|
1040
553
|
baseRef: metadata.baseRef,
|
|
@@ -1046,12 +559,15 @@ const executeReview = (binding, options) => Effect.gen(function* () {
|
|
|
1046
559
|
reviewedPathCount: anchorFiles.length,
|
|
1047
560
|
unresolvedFindings: activeFindings.map(toStoredFinding),
|
|
1048
561
|
unresolvedConcerns: activeConcerns.map(toStoredConcern),
|
|
562
|
+
unreviewedPaths,
|
|
563
|
+
unreviewedPasses: (core.unreviewedPasses ?? []).slice(0, 24),
|
|
564
|
+
settled,
|
|
1049
565
|
lastReviewMode: executionContext.mode
|
|
1050
566
|
}) : void 0;
|
|
1051
567
|
const continuity = stateCandidate === void 0 || executionContext?.stateAuthenticator === void 0 ? {
|
|
1052
568
|
state: void 0,
|
|
1053
569
|
marker: void 0,
|
|
1054
|
-
notice: executionContext
|
|
570
|
+
notice: executionContext !== void 0 && !carriedScopeFits ? `carried unreviewed scope (${unreviewedPaths.length} paths) exceeded the 100-path continuity bound` : executionContext?.stateAuthenticator?.status === "unavailable" ? executionContext.stateAuthenticator.unavailableReason ?? "authenticated continuity state is unavailable" : void 0
|
|
1055
571
|
} : yield* executionContext.stateAuthenticator.render(stateCandidate).pipe(Effect.match({
|
|
1056
572
|
onFailure: (error) => ({
|
|
1057
573
|
state: void 0,
|
|
@@ -1073,11 +589,10 @@ const executeReview = (binding, options) => Effect.gen(function* () {
|
|
|
1073
589
|
modelLabel: options.modelLabel,
|
|
1074
590
|
runUrl: options.runUrl,
|
|
1075
591
|
usage,
|
|
1076
|
-
|
|
1077
|
-
fingerprint: inputCoverage.status === "complete" && assurance.status === "settled" ? fingerprint : void 0,
|
|
1078
|
-
coverage,
|
|
592
|
+
fingerprint: skipFingerprint,
|
|
1079
593
|
inputCoverage,
|
|
1080
594
|
assurance,
|
|
595
|
+
unreviewedPaths,
|
|
1081
596
|
carriedFindings,
|
|
1082
597
|
carriedConcerns,
|
|
1083
598
|
reviewMode: executionContext?.mode,
|
|
@@ -1088,43 +603,127 @@ const executeReview = (binding, options) => Effect.gen(function* () {
|
|
|
1088
603
|
stateMarker: continuity.marker,
|
|
1089
604
|
stateNotice: continuity.notice
|
|
1090
605
|
});
|
|
1091
|
-
const
|
|
1092
|
-
if (!options.post) return ReviewRunOutcome.make({
|
|
606
|
+
const shared = {
|
|
1093
607
|
review,
|
|
1094
608
|
activeFindings,
|
|
1095
609
|
activeConcerns,
|
|
1096
|
-
coverage,
|
|
610
|
+
coverage: compatibilityCoverage(inputCoverage, assurance),
|
|
1097
611
|
inputCoverage,
|
|
1098
612
|
assurance,
|
|
613
|
+
unreviewedPaths,
|
|
1099
614
|
plan,
|
|
1100
|
-
turns:
|
|
1101
|
-
usage,
|
|
1102
|
-
...scope,
|
|
615
|
+
turns: core.turns,
|
|
616
|
+
...usage === void 0 ? {} : { usage },
|
|
1103
617
|
...executionContext === void 0 ? {} : {
|
|
1104
618
|
reviewMode: executionContext.mode,
|
|
1105
619
|
reviewReason: executionContext.reason
|
|
1106
620
|
},
|
|
1107
621
|
...continuity.state === void 0 ? {} : { state: continuity.state }
|
|
1108
|
-
}
|
|
622
|
+
};
|
|
623
|
+
if (!options.post) return ReviewRunOutcome.make(shared);
|
|
1109
624
|
const published = yield* (yield* ReviewPublisher).publish(plan);
|
|
1110
625
|
return ReviewRunOutcome.make({
|
|
626
|
+
...shared,
|
|
627
|
+
published
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
/**
|
|
631
|
+
* Execute one flat review with any explicit Agent Binding whose contract is
|
|
632
|
+
* `ReviewMission -> CodeReview`. The binding stays a parameter (D-027): tests
|
|
633
|
+
* pass scripted models, hosts pass live provider bindings, and the model
|
|
634
|
+
* Layer's requirements stay visible in this Effect's `R`.
|
|
635
|
+
*/
|
|
636
|
+
const executeReview = (binding, options) => Effect.gen(function* () {
|
|
637
|
+
const source = yield* PullRequestSource;
|
|
638
|
+
const metadata = yield* source.metadata;
|
|
639
|
+
const files = yield* source.changedFiles;
|
|
640
|
+
const anchorFiles = yield* source.anchorFiles;
|
|
641
|
+
const executionContext = Option.getOrUndefined(yield* Effect.serviceOption(ReviewExecutionContext));
|
|
642
|
+
const mission = buildReviewMission(metadata, files);
|
|
643
|
+
const fullMission = buildReviewMission(metadata, anchorFiles);
|
|
644
|
+
const fingerprint = options.signature === void 0 ? void 0 : yield* computeChangesetFingerprint(anchorFiles, options.signature(fullMission));
|
|
645
|
+
const budget = yield* makeUsageBudget(options.limits ?? reviewBudgetLimits);
|
|
646
|
+
const detached = yield* AgentRuntime.start(binding, mission, {
|
|
647
|
+
budget: toRunBudgetHook(budget),
|
|
648
|
+
estimateCostMicrousd: () => Effect.succeed(500)
|
|
649
|
+
});
|
|
650
|
+
const result = yield* detached.await;
|
|
651
|
+
const events = yield* detached.events;
|
|
652
|
+
const review = yield* Schema.decodeUnknownEffect(CodeReview)(result.output);
|
|
653
|
+
const assessment = assessFlatReview({
|
|
654
|
+
files,
|
|
655
|
+
totalFiles: executionContext?.totalFiles ?? metadata.totalChangedFiles,
|
|
656
|
+
anchorFiles,
|
|
657
|
+
totalAnchorFiles: metadata.totalChangedFiles,
|
|
658
|
+
events
|
|
659
|
+
});
|
|
660
|
+
const usage = yield* budget.snapshot;
|
|
661
|
+
return yield* settleReviewRun({
|
|
1111
662
|
review,
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
663
|
+
inputCoverage: assessment.inputCoverage,
|
|
664
|
+
assurance: assessment.assurance,
|
|
665
|
+
unreviewedPaths: assessment.unreviewedPaths,
|
|
666
|
+
turns: result.turns
|
|
667
|
+
}, {
|
|
668
|
+
metadata,
|
|
669
|
+
files,
|
|
670
|
+
anchorFiles,
|
|
671
|
+
fingerprint,
|
|
672
|
+
usage
|
|
673
|
+
}, options);
|
|
674
|
+
});
|
|
675
|
+
/**
|
|
676
|
+
* Execute one host-scheduled fan-out review: deterministic planning,
|
|
677
|
+
* independent discovery and verification child passes with bounded retries,
|
|
678
|
+
* and a host-composed review from verifier-confirmed candidates only. One
|
|
679
|
+
* budget observes every child pass, so the reported usage is whole-run.
|
|
680
|
+
*/
|
|
681
|
+
const executeFanOutReview = (binding, options) => Effect.gen(function* () {
|
|
682
|
+
const source = yield* PullRequestSource;
|
|
683
|
+
const metadata = yield* source.metadata;
|
|
684
|
+
const files = yield* source.changedFiles;
|
|
685
|
+
const anchorFiles = yield* source.anchorFiles;
|
|
686
|
+
const executionContext = Option.getOrUndefined(yield* Effect.serviceOption(ReviewExecutionContext));
|
|
687
|
+
const fullMission = buildReviewMission(metadata, anchorFiles);
|
|
688
|
+
const fingerprint = options.signature === void 0 ? void 0 : yield* computeChangesetFingerprint(anchorFiles, options.signature(fullMission));
|
|
689
|
+
const budget = yield* makeUsageBudget(options.limits ?? fanOutReviewBudgetLimits);
|
|
690
|
+
const totalFiles = executionContext?.totalFiles ?? metadata.totalChangedFiles;
|
|
691
|
+
const pipeline = yield* runFanOutReview(binding, {
|
|
692
|
+
files,
|
|
693
|
+
anchorFiles,
|
|
694
|
+
totalChangedFiles: totalFiles,
|
|
695
|
+
maxFindings: options.maxFindings,
|
|
696
|
+
budget: toRunBudgetHook(budget),
|
|
697
|
+
...executionContext !== void 0 && executionContext.retryPaths.length > 0 ? { retry: {
|
|
698
|
+
paths: executionContext.retryPaths,
|
|
699
|
+
stages: executionContext.retryStages
|
|
700
|
+
} } : {}
|
|
1127
701
|
});
|
|
702
|
+
const inputCoverage = fanOutInputCoverage({
|
|
703
|
+
plan: pipeline.plan,
|
|
704
|
+
files,
|
|
705
|
+
totalFiles,
|
|
706
|
+
anchorFiles,
|
|
707
|
+
totalAnchorFiles: metadata.totalChangedFiles
|
|
708
|
+
});
|
|
709
|
+
const usage = yield* budget.snapshot;
|
|
710
|
+
return yield* settleReviewRun({
|
|
711
|
+
review: pipeline.review,
|
|
712
|
+
inputCoverage,
|
|
713
|
+
assurance: pipeline.assurance,
|
|
714
|
+
unreviewedPaths: pipeline.unreviewedPaths,
|
|
715
|
+
unreviewedPasses: pipeline.unreviewedPasses.slice(0, 24).map((pass) => StoredUnreviewedPass.make({
|
|
716
|
+
stage: pass.stage,
|
|
717
|
+
paths: pass.paths.slice(0, 12)
|
|
718
|
+
})),
|
|
719
|
+
turns: pipeline.turns
|
|
720
|
+
}, {
|
|
721
|
+
metadata,
|
|
722
|
+
files,
|
|
723
|
+
anchorFiles,
|
|
724
|
+
fingerprint,
|
|
725
|
+
usage
|
|
726
|
+
}, options);
|
|
1128
727
|
});
|
|
1129
728
|
//#endregion
|
|
1130
729
|
//#region src/internal/factory.ts
|
|
@@ -1164,8 +763,8 @@ const makeReviewSnapshot = (ignore) => provideIgnore(Effect.gen(function* () {
|
|
|
1164
763
|
* Build the flat reviewer: one bounded read-only agent over the whole
|
|
1165
764
|
* changeset. Returns the model-agnostic definition, the explicit binding, and
|
|
1166
765
|
* a `run` whose error and requirement channels stay fully inferred — the
|
|
1167
|
-
* pull-request source, the publisher, extra tool handlers,
|
|
1168
|
-
* Layer's requirements all remain visible to the caller.
|
|
766
|
+
* pull-request source, the publisher, `Crypto.Crypto`, extra tool handlers,
|
|
767
|
+
* and the Model Layer's requirements all remain visible to the caller.
|
|
1169
768
|
*/
|
|
1170
769
|
const make = (options) => {
|
|
1171
770
|
const extraTools = options.extraTools ?? EMPTY_TOOLS;
|
|
@@ -1211,9 +810,7 @@ const make = (options) => {
|
|
|
1211
810
|
maxFindings: clampMaxFindings(options.maxFindings),
|
|
1212
811
|
signature,
|
|
1213
812
|
modelLabel: options.modelLabel,
|
|
1214
|
-
runUrl: runOptions.runUrl
|
|
1215
|
-
usageScope: "run",
|
|
1216
|
-
reviewShape: "flat"
|
|
813
|
+
runUrl: runOptions.runUrl
|
|
1217
814
|
}).pipe(Effect.provide(Layer.mergeAll(ReviewToolkitLayer, IdGenerator.layer)), Effect.scoped), options.ignore);
|
|
1218
815
|
return {
|
|
1219
816
|
definition,
|
|
@@ -1229,56 +826,47 @@ const make = (options) => {
|
|
|
1229
826
|
};
|
|
1230
827
|
};
|
|
1231
828
|
/**
|
|
1232
|
-
* Build the fan-out reviewer:
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
829
|
+
* Build the fan-out reviewer: host code schedules host-planned general and
|
|
830
|
+
* specialist discovery plus independent candidate verification directly as
|
|
831
|
+
* bounded child runs — there is no coordinator model and no delegation tool,
|
|
832
|
+
* so review assurance cannot fail on scheduling compliance. Host code
|
|
833
|
+
* composes publication only from exactly confirmed candidates. Child
|
|
834
|
+
* execution bounds are packaged and not configurable here.
|
|
1238
835
|
*/
|
|
1239
836
|
const makeFanOut = (options) => {
|
|
1240
|
-
const
|
|
1241
|
-
guidance: options.guidance,
|
|
1242
|
-
maxFindings: options.maxFindings
|
|
1243
|
-
});
|
|
1244
|
-
const binding = Object.freeze({
|
|
1245
|
-
definition: suite.parent,
|
|
1246
|
-
model: options.model
|
|
1247
|
-
});
|
|
837
|
+
const child = makeFileReviewerDefinition({ guidance: options.guidance });
|
|
1248
838
|
const childBinding = Object.freeze({
|
|
1249
|
-
definition:
|
|
839
|
+
definition: child,
|
|
1250
840
|
model: options.model
|
|
1251
841
|
});
|
|
1252
842
|
const guidanceLines = options.guidance === void 0 ? [] : typeof options.guidance === "string" ? [options.guidance] : options.guidance;
|
|
1253
843
|
const signature = (mission) => [
|
|
1254
|
-
|
|
844
|
+
"pr-review-fan-out-host-scheduled-v1",
|
|
845
|
+
JSON.stringify(Schema.encodeSync(ReviewMission)(mission)),
|
|
1255
846
|
`childGuidance=${JSON.stringify(guidanceLines)}`,
|
|
847
|
+
`maxFindings=${clampMaxFindings(options.maxFindings)}`,
|
|
1256
848
|
`applyVerdict=${String(options.applyVerdict ?? false)}`,
|
|
1257
849
|
...options.modelLabel === void 0 ? [] : [`model=${options.modelLabel}`]
|
|
1258
850
|
].join(" ");
|
|
1259
851
|
const profileSignature = (_mission) => [
|
|
1260
|
-
"pr-review-profile-
|
|
852
|
+
"pr-review-profile-v4-host-scheduled",
|
|
1261
853
|
JSON.stringify(guidanceLines),
|
|
1262
854
|
JSON.stringify(options.ignore ?? []),
|
|
1263
855
|
`maxFindings=${clampMaxFindings(options.maxFindings)}`,
|
|
1264
856
|
`applyVerdict=${String(options.applyVerdict ?? false)}`,
|
|
1265
857
|
...options.modelLabel === void 0 ? [] : [`model=${options.modelLabel}`]
|
|
1266
|
-
].join("
|
|
1267
|
-
const
|
|
1268
|
-
const run = (runOptions = {}) => provideIgnore(executeReview(binding, {
|
|
858
|
+
].join(" ");
|
|
859
|
+
const run = (runOptions = {}) => provideIgnore(executeFanOutReview(childBinding, {
|
|
1269
860
|
post: runOptions.post ?? false,
|
|
1270
861
|
applyVerdict: options.applyVerdict ?? false,
|
|
1271
862
|
limits: options.budget ?? fanOutReviewBudgetLimits,
|
|
1272
863
|
maxFindings: clampMaxFindings(options.maxFindings),
|
|
1273
864
|
signature,
|
|
1274
865
|
modelLabel: options.modelLabel,
|
|
1275
|
-
runUrl: runOptions.runUrl
|
|
1276
|
-
|
|
1277
|
-
reviewShape: "fan-out"
|
|
1278
|
-
}).pipe(Effect.provide(Layer.mergeAll(FanOutCoordinatorToolkitLayer, delegationLayer, IdGenerator.layer)), Effect.scoped), options.ignore);
|
|
866
|
+
runUrl: runOptions.runUrl
|
|
867
|
+
}).pipe(Effect.provide(IdGenerator.layer), Effect.scoped), options.ignore);
|
|
1279
868
|
return {
|
|
1280
|
-
definition:
|
|
1281
|
-
binding,
|
|
869
|
+
definition: child,
|
|
1282
870
|
childBinding,
|
|
1283
871
|
run,
|
|
1284
872
|
fingerprint: makeFingerprint(signature, options.ignore),
|
|
@@ -1620,6 +1208,6 @@ const openAiClientLayer = OpenAiClient.layerConfig({ apiKey: Config.redacted(PRO
|
|
|
1620
1208
|
/** The Anthropic client Layer, credential from `ANTHROPIC_API_KEY`. */
|
|
1621
1209
|
const anthropicClientLayer = AnthropicClient.layerConfig({ apiKey: Config.redacted(PROVIDER_CREDENTIAL_ENV.anthropic) }).pipe(Layer.provide(FetchHttpClient.layer));
|
|
1622
1210
|
//#endregion
|
|
1623
|
-
export {
|
|
1211
|
+
export { reviewBudgetLimits as A, ignoringPullRequestSourceLayer as B, PrReview as C, executeFanOutReview as D, enforceFindingsBound as E, estimateReviewEffort as F, resolveEffortRung as G, InvalidEffortInput as H, planPublication as I, planWalkthrough as L, ReviewCommentDraft as M, ReviewEvent as N, executeReview as O, ReviewPublicationPlan as P, renderAgentPrompt as R, renderProgressSettleBody as S, buildReviewMission as T, isEffortPosition as U, EFFORT_ALIASES as V, parseEffortPosition as W, gitHubReviewProgressLayer as _, anthropicClientLayer as a, renderProgressBeginBody as b, makeOpenAiReviewModel as c, ReviewTargetUnresolved as d, gitHubReviewLayers as f, ReviewProgressReporter as g, PROGRESS_COMMENT_MARKER_PREFIX as h, PROVIDER_EFFORT_RUNGS as i, AGENT_PROMPT_PREAMBLE as j, fanOutReviewBudgetLimits as k, openAiClientLayer as l, resolveReviewTarget as m, DEFAULT_PROVIDER as n, describeReviewModel as o, readGitHubEvent as p, PROVIDER_CREDENTIAL_ENV as r, makeAnthropicReviewModel as s, DEFAULT_MODEL as t, GitHubEventWire as u, noopReviewProgressReporterLayer as v, ReviewRunOutcome as w, renderProgressClaimMarker as x, parseProgressClaim as y, compileIgnoreGlobs as z };
|
|
1624
1212
|
|
|
1625
|
-
//# sourceMappingURL=providers-
|
|
1213
|
+
//# sourceMappingURL=providers-2Jao2ZAX.mjs.map
|