@effect-agent/pr-review 0.1.0-beta.26 → 0.1.0-beta.28
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 +26 -3
- package/dist/action.d.mts +11 -8
- package/dist/action.mjs +24 -9
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +7 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/{fan-out-CMEsbFLk.d.mts → fan-out-C3yG1cx3.d.mts} +45 -20
- package/dist/{github-NjgxGqwM.mjs → github-CCuLgyqb.mjs} +254 -81
- package/dist/github-CCuLgyqb.mjs.map +1 -0
- package/dist/index.d.mts +164 -13
- package/dist/index.mjs +3 -3
- package/dist/{providers-CODZQCmL.mjs → providers-Br9FRn7j.mjs} +35 -12
- package/dist/providers-Br9FRn7j.mjs.map +1 -0
- package/dist/testing.d.mts +3 -3
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +48 -11
- package/src/cli.ts +14 -2
- package/src/internal/action-entry.ts +1 -0
- package/src/internal/factory.ts +2 -1
- package/src/internal/fan-out.ts +150 -66
- package/src/internal/fixtures.ts +3 -3
- package/src/internal/github.ts +169 -22
- package/src/internal/providers.ts +47 -8
- package/src/internal/review-state.ts +85 -47
- package/src/internal/run.ts +8 -3
- package/dist/github-NjgxGqwM.mjs.map +0 -1
- package/dist/providers-CODZQCmL.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ truncated run posts nothing.
|
|
|
15
15
|
import { Effect, Layer } from "effect";
|
|
16
16
|
import {
|
|
17
17
|
PrReview,
|
|
18
|
+
describeReviewModel,
|
|
18
19
|
fullReviewExecutionContextLayer,
|
|
19
20
|
gitHubReviewLayers,
|
|
20
21
|
resolveReviewTarget,
|
|
@@ -47,6 +48,21 @@ Anthropic is equally supported
|
|
|
47
48
|
(`makeAnthropicReviewModel`, `anthropicClientLayer`), and the factory accepts
|
|
48
49
|
any Effect AI Model.
|
|
49
50
|
|
|
51
|
+
The built-in OpenAI binding can opt each request into Fast mode:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const reviewer = PrReview.make({
|
|
55
|
+
model: makeOpenAiReviewModel("gpt-5.6-sol", undefined, "fast"),
|
|
56
|
+
modelLabel: describeReviewModel("openai", "gpt-5.6-sol", undefined, "fast"),
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`fast` is the only packaged service-tier value. The helper sends it as the
|
|
61
|
+
OpenAI Responses `service_tier`; omitting it leaves the request unset so the
|
|
62
|
+
OpenAI project default applies. The Action and CLI reject a service tier when
|
|
63
|
+
the selected provider is not OpenAI. The model label includes the tier and
|
|
64
|
+
therefore changes both ordinary and profile fingerprints.
|
|
65
|
+
|
|
50
66
|
## Adapt
|
|
51
67
|
|
|
52
68
|
```ts
|
|
@@ -169,12 +185,19 @@ gate.
|
|
|
169
185
|
## Action and CLI
|
|
170
186
|
|
|
171
187
|
The prebuilt GitHub Action handles signed incremental state, fail-closed scope selection, sticky
|
|
172
|
-
progress, stale-review retirement, and final audits.
|
|
173
|
-
|
|
188
|
+
progress, stale-review retirement, and final audits. Failed stages remain attached to their exact
|
|
189
|
+
unchanged paths; failed verification reopens discovery only for its paths because candidates are
|
|
190
|
+
not persisted. See its [setup and behavior guide](../../action/README.md) and
|
|
191
|
+
[input reference](../../action/action.yml). Custom hosts can import `runReviewAction` from
|
|
174
192
|
`@effect-agent/pr-review/action`.
|
|
175
193
|
|
|
194
|
+
For an amended or force-pushed head, the GitHub adapter compares complete commit tree snapshots
|
|
195
|
+
across a bounded PR and continuity path set. Selection then uses the current full PR records, so
|
|
196
|
+
patches and rename metadata remain intact. An unavailable, malformed, or truncated tree falls back
|
|
197
|
+
to the full PR and records the cause in the review reason.
|
|
198
|
+
|
|
176
199
|
Run the CLI with
|
|
177
|
-
`bun src/cli.ts --repo owner/name --pr 123 [--post] [--provider anthropic] [--fan-out]`.
|
|
200
|
+
`bun src/cli.ts --repo owner/name --pr 123 [--post] [--provider anthropic] [--service-tier fast] [--fan-out]`.
|
|
178
201
|
|
|
179
202
|
## Bounds, spelled out
|
|
180
203
|
|
package/dist/action.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EffortPosition, InvalidEffortInput, ReviewProgressReporter, ReviewProvider, ReviewRunOutcome, ReviewTargetUnresolved, RunReviewOptions } from "./index.mjs";
|
|
1
|
+
import { Hr as ChangedFile, L as PriorReviews, Lr as PullRequestMetadata, P as GitHubApiFailure, Q as ReviewRetirementHost, Qt as ReviewAdjudicationHost, Rr as PullRequestSource, _n as ReviewExecutionContext, wn as ReviewStateAuthenticator, yn as ReviewMode, z as ReviewPublisher, zr as PullRequestSourceFailure } from "./fan-out-C3yG1cx3.mjs";
|
|
2
|
+
import { EffortPosition, InvalidEffortInput, OpenAiServiceTier, ReviewProgressReporter, ReviewProvider, ReviewRunOutcome, ReviewTargetUnresolved, RunReviewOptions, UnsupportedServiceTierProvider } from "./index.mjs";
|
|
3
3
|
import { Config, Effect, FileSystem, Schema } from "effect";
|
|
4
4
|
import { BudgetExceeded } from "effect-agent";
|
|
5
5
|
//#region src/action.d.ts
|
|
@@ -25,6 +25,7 @@ interface ResolvedActionInputs {
|
|
|
25
25
|
readonly provider: ReviewProvider;
|
|
26
26
|
readonly model: string | undefined;
|
|
27
27
|
readonly effort: EffortPosition | undefined;
|
|
28
|
+
readonly serviceTier: OpenAiServiceTier | undefined;
|
|
28
29
|
readonly post: boolean;
|
|
29
30
|
readonly applyVerdict: boolean;
|
|
30
31
|
readonly fanOut: boolean;
|
|
@@ -40,9 +41,10 @@ interface ResolvedActionInputs {
|
|
|
40
41
|
}
|
|
41
42
|
/** Read the PR_REVIEW_* input surface (all optional, all defaulted). */
|
|
42
43
|
declare const resolveActionInputs: () => Effect.Effect<{
|
|
43
|
-
provider:
|
|
44
|
+
provider: "anthropic" | "openai";
|
|
44
45
|
model: string | undefined;
|
|
45
46
|
effort: number | undefined;
|
|
47
|
+
serviceTier: "fast" | undefined;
|
|
46
48
|
post: boolean;
|
|
47
49
|
applyVerdict: boolean;
|
|
48
50
|
fanOut: boolean;
|
|
@@ -55,7 +57,7 @@ declare const resolveActionInputs: () => Effect.Effect<{
|
|
|
55
57
|
skipUnchanged: boolean;
|
|
56
58
|
retireStaleReviews: boolean;
|
|
57
59
|
progressComment: boolean;
|
|
58
|
-
}, Config.ConfigError | InvalidEffortInput | InvalidMaxDurationInput, never>;
|
|
60
|
+
}, Config.ConfigError | InvalidEffortInput | InvalidMaxDurationInput | UnsupportedServiceTierProvider, never>;
|
|
59
61
|
/** A guidance file larger than this is refused, never silently truncated. */
|
|
60
62
|
declare const MAX_GUIDANCE_FILE_CHARS = 20000;
|
|
61
63
|
declare const GuidanceFileUnreadable_base: Schema.Class<GuidanceFileUnreadable, Schema.TaggedStruct<"GuidanceFileUnreadable", {
|
|
@@ -175,15 +177,16 @@ declare const reviewActionProgram: Effect.Effect<{
|
|
|
175
177
|
} | {
|
|
176
178
|
_tag: "Completed";
|
|
177
179
|
outcome: ReviewRunOutcome;
|
|
178
|
-
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, import("effect/Crypto").Crypto | FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
180
|
+
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextBudgetError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError | UnsupportedServiceTierProvider, import("effect/Crypto").Crypto | FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
179
181
|
/** Run the packaged action program on Node; the bundled action entrypoint. */
|
|
180
182
|
declare const main: () => void;
|
|
181
183
|
/** The packaged GitHub Actions surface. */
|
|
182
184
|
declare const PrReviewAction: {
|
|
183
185
|
readonly inputs: () => Effect.Effect<{
|
|
184
|
-
provider:
|
|
186
|
+
provider: "anthropic" | "openai";
|
|
185
187
|
model: string | undefined;
|
|
186
188
|
effort: number | undefined;
|
|
189
|
+
serviceTier: "fast" | undefined;
|
|
187
190
|
post: boolean;
|
|
188
191
|
applyVerdict: boolean;
|
|
189
192
|
fanOut: boolean;
|
|
@@ -196,7 +199,7 @@ declare const PrReviewAction: {
|
|
|
196
199
|
skipUnchanged: boolean;
|
|
197
200
|
retireStaleReviews: boolean;
|
|
198
201
|
progressComment: boolean;
|
|
199
|
-
}, Config.ConfigError | InvalidEffortInput | InvalidMaxDurationInput, never>;
|
|
202
|
+
}, Config.ConfigError | InvalidEffortInput | InvalidMaxDurationInput | UnsupportedServiceTierProvider, never>;
|
|
200
203
|
readonly run: typeof runReviewAction;
|
|
201
204
|
readonly program: Effect.Effect<{
|
|
202
205
|
_tag: "Skipped";
|
|
@@ -204,7 +207,7 @@ declare const PrReviewAction: {
|
|
|
204
207
|
} | {
|
|
205
208
|
_tag: "Completed";
|
|
206
209
|
outcome: ReviewRunOutcome;
|
|
207
|
-
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, import("effect/Crypto").Crypto | FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
210
|
+
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextBudgetError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError | UnsupportedServiceTierProvider, import("effect/Crypto").Crypto | FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
208
211
|
readonly main: typeof main;
|
|
209
212
|
};
|
|
210
213
|
//#endregion
|
package/dist/action.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Cr as normalizeRepoRelativePath, Dn as selectReviewRange, En as isLineageAncestor, Mn as validateReviewState, Nn as webCryptoReviewStateAuthenticatorLayer, On as selectedPullRequestSourceLayer, St as splitCarriedScope, Tn as fullReviewSelection, Yt as retireStaleReviews, a as PriorReviews, bn as concernIdentity, br as PullRequestSource, in as ReviewHeadComparison, jn as unavailableReviewStateAuthenticatorLayer, jt as collectReviewAdjudications, ln as ReviewStateAuthenticator, rn as ReviewExecutionContext, vn as adjudicationIdentity, xn as findingIdentity } from "./github-CCuLgyqb.mjs";
|
|
2
|
+
import { D as PrReview, J as parseEffortPosition, K as InvalidEffortInput, N as fanOutReviewBudgetLimits, P as reviewBudgetLimits, _ as readGitHubEvent, b as ReviewProgressReporter, c as anthropicClientLayer, d as makeOpenAiReviewModel, f as openAiClientLayer, g as gitHubReviewLayers, l as describeReviewModel, n as DEFAULT_PROVIDER, p as validateReviewServiceTier, u as makeAnthropicReviewModel, v as resolveReviewTarget } from "./providers-Br9FRn7j.mjs";
|
|
3
3
|
import { n as compactReviewLoggingLayer } from "./logging-Q4j0oub-.mjs";
|
|
4
4
|
import { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from "effect";
|
|
5
5
|
import { BudgetExceeded, UsageBudgetLimits } from "effect-agent";
|
|
@@ -36,6 +36,7 @@ const resolveActionInputs = Effect.fn("resolveActionInputs")(function* () {
|
|
|
36
36
|
effort = parseEffortPosition(effortRaw.value);
|
|
37
37
|
if (effort === void 0) return yield* InvalidEffortInput.make({ input: effortRaw.value });
|
|
38
38
|
}
|
|
39
|
+
const serviceTier = yield* validateReviewServiceTier(provider, Option.getOrUndefined(yield* Config.option(Config.literals(["fast"], "PR_REVIEW_SERVICE_TIER"))));
|
|
39
40
|
const maxDurationMinutes = Option.getOrUndefined(yield* Config.option(Config.int("PR_REVIEW_MAX_DURATION_MINUTES")));
|
|
40
41
|
if (maxDurationMinutes !== void 0 && maxDurationMinutes <= 0) return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });
|
|
41
42
|
const post = yield* Config.boolean("PR_REVIEW_POST").pipe(Config.withDefault(true));
|
|
@@ -53,6 +54,7 @@ const resolveActionInputs = Effect.fn("resolveActionInputs")(function* () {
|
|
|
53
54
|
provider,
|
|
54
55
|
model: Option.getOrUndefined(model),
|
|
55
56
|
effort,
|
|
57
|
+
serviceTier,
|
|
56
58
|
post,
|
|
57
59
|
applyVerdict,
|
|
58
60
|
fanOut,
|
|
@@ -296,6 +298,7 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
|
|
|
296
298
|
let comparison;
|
|
297
299
|
let baseComparison;
|
|
298
300
|
let contentComparison;
|
|
301
|
+
let contentComparisonFailure;
|
|
299
302
|
if ((options.reviewMode ?? "incremental") === "incremental" && recovered.state !== void 0) {
|
|
300
303
|
if (recovered.state.reviewedHeadSha === metadata.headSha) comparison = ReviewHeadComparison.make({
|
|
301
304
|
status: "identical",
|
|
@@ -320,11 +323,22 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
|
|
|
320
323
|
});
|
|
321
324
|
}
|
|
322
325
|
if (recovered.state.reviewedHeadSha !== metadata.headSha && (comparison === void 0 || !isLineageAncestor(comparison, recovered.state, metadata.headSha))) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
const comparisonPaths = new Set(fullFiles.flatMap((file) => file.previousPath === void 0 ? [file.path] : [file.path, file.previousPath]));
|
|
327
|
+
for (const finding of recovered.state.unresolvedFindings) comparisonPaths.add(finding.path);
|
|
328
|
+
for (const concern of recovered.state.unresolvedConcerns) for (const path of concern.evidencePaths ?? []) comparisonPaths.add(path);
|
|
329
|
+
for (const path of recovered.state.unreviewedPaths) comparisonPaths.add(path);
|
|
330
|
+
const treeResult = yield* history.compareTrees(recovered.state.reviewedHeadSha, metadata.headSha, [...comparisonPaths]).pipe(Effect.match({
|
|
331
|
+
onFailure: (failure) => ({
|
|
332
|
+
comparison: void 0,
|
|
333
|
+
failure: failure.reason
|
|
334
|
+
}),
|
|
335
|
+
onSuccess: (treeComparison) => ({
|
|
336
|
+
comparison: treeComparison,
|
|
337
|
+
failure: void 0
|
|
338
|
+
})
|
|
339
|
+
}));
|
|
340
|
+
contentComparison = treeResult.comparison;
|
|
341
|
+
contentComparisonFailure = treeResult.failure;
|
|
328
342
|
}
|
|
329
343
|
}
|
|
330
344
|
selection = {
|
|
@@ -337,6 +351,7 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
|
|
|
337
351
|
comparison,
|
|
338
352
|
baseComparison,
|
|
339
353
|
contentComparison,
|
|
354
|
+
contentComparisonFailure,
|
|
340
355
|
lookupFailure: recovered.failure
|
|
341
356
|
}),
|
|
342
357
|
stateAuthenticator
|
|
@@ -432,7 +447,7 @@ const reviewActionProgram = Effect.gen(function* () {
|
|
|
432
447
|
onSome: webCryptoReviewStateAuthenticatorLayer
|
|
433
448
|
});
|
|
434
449
|
const guidance = yield* resolveGuidance(inputs);
|
|
435
|
-
const modelLabel = describeReviewModel(inputs.provider, inputs.model, inputs.effort);
|
|
450
|
+
const modelLabel = describeReviewModel(inputs.provider, inputs.model, inputs.effort, inputs.serviceTier);
|
|
436
451
|
const defaults = inputs.fanOut ? fanOutReviewBudgetLimits : reviewBudgetLimits;
|
|
437
452
|
const budget = inputs.maxDurationMinutes === void 0 ? defaults : UsageBudgetLimits.make({
|
|
438
453
|
...defaults,
|
|
@@ -465,7 +480,7 @@ const reviewActionProgram = Effect.gen(function* () {
|
|
|
465
480
|
});
|
|
466
481
|
return yield* runReviewAction(reviewer, harness).pipe(Effect.provide(Layer.merge(stateAuthenticatorLayer, anthropicClientLayer)));
|
|
467
482
|
}
|
|
468
|
-
const model = makeOpenAiReviewModel(inputs.model, inputs.effort);
|
|
483
|
+
const model = makeOpenAiReviewModel(inputs.model, inputs.effort, inputs.serviceTier);
|
|
469
484
|
const reviewer = inputs.fanOut ? PrReview.makeFanOut({
|
|
470
485
|
...shared,
|
|
471
486
|
model
|
package/dist/action.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.mjs","names":[],"sources":["../src/action.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from \"effect\";\nimport { BudgetExceeded, UsageBudgetLimits } from \"effect-agent\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { collectReviewAdjudications } from \"./internal/adjudication.ts\";\nimport { splitCarriedScope } from \"./internal/coverage.ts\";\nimport type { ChangedFile } from \"./internal/diff.ts\";\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { readGitHubEvent, resolveReviewTarget, gitHubReviewLayers } from \"./internal/github-env.ts\";\nimport { PriorReviews } from \"./internal/github.ts\";\nimport { compactReviewLoggingLayer } from \"./internal/logging.ts\";\nimport { ReviewProgressReporter } from \"./internal/progress.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { retireStaleReviews } from \"./internal/retirement.ts\";\nimport {\n adjudicationIdentity,\n concernIdentity,\n findingIdentity,\n fullReviewSelection,\n ReviewExecutionContext,\n ReviewHeadComparison,\n ReviewStateAuthenticator,\n isLineageAncestor,\n type ReviewMode,\n type ReviewState,\n selectReviewRange,\n selectedPullRequestSourceLayer,\n unavailableReviewStateAuthenticatorLayer,\n validateReviewState,\n webCryptoReviewStateAuthenticatorLayer,\n} from \"./internal/review-state.ts\";\nimport { fanOutReviewBudgetLimits, reviewBudgetLimits } from \"./internal/run.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\nimport {\n normalizeRepoRelativePath,\n PullRequestSource,\n type PullRequestMetadata,\n} from \"./internal/source.ts\";\n\n// ---------------------------------------------------------------------------\n// The GitHub Actions entrypoint (deployment class E: one bounded ephemeral\n// run, no exactly-once posting). Bounded continuity state travels in GitHub\n// review bodies and is validated before reuse. Inputs arrive as\n// PR_REVIEW_* environment variables set by the action manifest; the target\n// pull request comes from the standard Actions event environment. A non-PR\n// or draft event is a typed skip — green job, nothing posted. Publication\n// happens only after the run settles, so a failed or truncated run posts\n// nothing.\n// ---------------------------------------------------------------------------\n\nexport const ReviewCheckConclusion = Schema.Literals([\"success\", \"blocking\", \"incomplete\"]);\nexport type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;\n\n/** The host-derived blocking or incomplete conclusion failed the check. */\nexport class ReviewGateFailed extends Schema.TaggedError<ReviewGateFailed>()(\"ReviewGateFailed\", {\n conclusion: Schema.Literals([\"blocking\", \"incomplete\"]),\n reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1_000))).check(\n Schema.isMinLength(1),\n ),\n}) {\n override get message() {\n return `Review check concluded '${this.conclusion}': ${this.reasons.join(\"; \")}`;\n }\n}\n\n/** A configured max-duration that cannot bound a run; configuration faults fail loudly. */\nexport class InvalidMaxDurationInput extends Schema.TaggedError<InvalidMaxDurationInput>()(\n \"InvalidMaxDurationInput\",\n {\n minutes: Schema.Int,\n },\n) {\n override get message() {\n return `Invalid max-duration-minutes '${this.minutes}': expected a positive number of minutes.`;\n }\n}\n\n/** Everything the packaged action reads from its environment. */\nexport interface ResolvedActionInputs {\n readonly provider: ReviewProvider;\n readonly model: string | undefined;\n readonly effort: EffortPosition | undefined;\n readonly post: boolean;\n readonly applyVerdict: boolean;\n readonly fanOut: boolean;\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n readonly ignore: ReadonlyArray<string>;\n readonly maxFindings: number | undefined;\n readonly maxDurationMinutes: number | undefined;\n readonly reviewMode: ReviewMode;\n readonly skipUnchanged: boolean;\n readonly retireStaleReviews: boolean;\n readonly progressComment: boolean;\n}\n\n/** Read the PR_REVIEW_* input surface (all optional, all defaulted). */\nexport const resolveActionInputs = Effect.fn(\"resolveActionInputs\")(function* () {\n const provider = yield* Config.literals([\"openai\", \"anthropic\"], \"PR_REVIEW_PROVIDER\").pipe(\n Config.withDefault<ReviewProvider>(DEFAULT_PROVIDER),\n );\n const model = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_MODEL\"));\n const effortRaw = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_EFFORT\"));\n let effort: EffortPosition | undefined;\n if (Option.isSome(effortRaw)) {\n effort = parseEffortPosition(effortRaw.value);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw.value });\n }\n }\n const maxDurationMinutes = Option.getOrUndefined(\n yield* Config.option(Config.int(\"PR_REVIEW_MAX_DURATION_MINUTES\")),\n );\n if (maxDurationMinutes !== undefined && maxDurationMinutes <= 0) {\n return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });\n }\n const post = yield* Config.boolean(\"PR_REVIEW_POST\").pipe(Config.withDefault(true));\n const applyVerdict = yield* Config.boolean(\"PR_REVIEW_APPLY_VERDICT\").pipe(\n Config.withDefault(false),\n );\n const fanOut = yield* Config.boolean(\"PR_REVIEW_FAN_OUT\").pipe(Config.withDefault(true));\n const guidance = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE\"));\n const guidanceFile = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE_FILE\"));\n const ignoreRaw = yield* Config.string(\"PR_REVIEW_IGNORE\").pipe(Config.withDefault(\"\"));\n const maxFindings = yield* Config.option(Config.int(\"PR_REVIEW_MAX_FINDINGS\"));\n const reviewMode = yield* Config.literals([\"incremental\", \"final\"], \"PR_REVIEW_MODE\").pipe(\n Config.withDefault<ReviewMode>(\"incremental\"),\n );\n const skipUnchanged = yield* Config.boolean(\"PR_REVIEW_SKIP_UNCHANGED\").pipe(\n Config.withDefault(true),\n );\n const retireStaleReviews = yield* Config.boolean(\"PR_REVIEW_RETIRE_STALE_REVIEWS\").pipe(\n Config.withDefault(true),\n );\n const progressComment = yield* Config.boolean(\"PR_REVIEW_PROGRESS_COMMENT\").pipe(\n Config.withDefault(true),\n );\n return {\n provider,\n model: Option.getOrUndefined(model),\n effort,\n post,\n applyVerdict,\n fanOut,\n guidance: Option.getOrUndefined(guidance),\n guidanceFile: Option.getOrUndefined(guidanceFile),\n ignore: ignoreRaw\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(maxFindings),\n maxDurationMinutes,\n reviewMode,\n skipUnchanged,\n retireStaleReviews,\n progressComment,\n } satisfies ResolvedActionInputs;\n});\n\n/** A guidance file larger than this is refused, never silently truncated. */\nexport const MAX_GUIDANCE_FILE_CHARS = 20_000;\n\n/** A configured guidance file could not be used; configuration faults fail loudly. */\nexport class GuidanceFileUnreadable extends Schema.TaggedError<GuidanceFileUnreadable>()(\n \"GuidanceFileUnreadable\",\n {\n path: Schema.String,\n reason: Schema.String,\n },\n) {\n override get message() {\n return `Cannot use guidance file '${this.path}': ${this.reason}`;\n }\n}\n\n/**\n * Resolve the effective review guidance: the committed guidance file (a\n * repository-owned review profile) first, with any inline guidance appended.\n * A configured-but-unreadable file fails typed — a review silently running\n * without its profile would be worse than a red job.\n */\nexport const resolveGuidance = Effect.fn(\"resolveGuidance\")(function* (inputs: {\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n}) {\n const filePath = inputs.guidanceFile;\n if (filePath === undefined) return inputs.guidance;\n // The profile path is operator configuration, but it stays fail-closed\n // like every other path in this package: workspace-relative only, and the\n // size bound is checked before the file is read into memory.\n const relative = yield* normalizeRepoRelativePath(filePath).pipe(\n Effect.mapError((violation) =>\n GuidanceFileUnreadable.make({ path: filePath, reason: violation.reason }),\n ),\n );\n const fs = yield* FileSystem.FileSystem;\n const unreadable = (error: { readonly _tag: string; readonly message: string }) =>\n GuidanceFileUnreadable.make({\n path: relative,\n reason: `${error._tag}: ${error.message}`.slice(0, 2_048),\n });\n const stat = yield* fs.stat(relative).pipe(Effect.mapError(unreadable));\n const oversized = GuidanceFileUnreadable.make({\n path: relative,\n reason: `File is larger than the ${MAX_GUIDANCE_FILE_CHARS}-character guidance bound.`,\n });\n if (stat.size > BigInt(MAX_GUIDANCE_FILE_CHARS) * 4n) {\n return yield* oversized;\n }\n const content = yield* fs.readFileString(relative).pipe(Effect.mapError(unreadable));\n if (content.length > MAX_GUIDANCE_FILE_CHARS) {\n return yield* oversized;\n }\n const combined = [content.trim(), inputs.guidance ?? \"\"]\n .filter((part) => part.length > 0)\n .join(\"\\n\");\n return combined.length > 0 ? combined : undefined;\n});\n\n/** One step-output line; values must be single-line by construction. */\nconst outputLine = (name: string, value: string): string =>\n `${name}=${value.replaceAll(\"\\n\", \" \").slice(0, 1_000)}\\n`;\n\n/** Append step outputs to GITHUB_OUTPUT when present (no-op locally). */\nexport const writeActionOutputs = Effect.fn(\"writeActionOutputs\")(function* (\n entries: ReadonlyArray<readonly [name: string, value: string]>,\n) {\n const outputPath = yield* Config.string(\"GITHUB_OUTPUT\").pipe(Config.withDefault(\"\"));\n if (outputPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(\n outputPath,\n entries.map(([name, value]) => outputLine(name, value)).join(\"\"),\n {\n flag: \"a\",\n },\n );\n});\n\n/** Append markdown to the GITHUB_STEP_SUMMARY report when present (no-op locally). */\nexport const writeStepSummary = Effect.fn(\"writeStepSummary\")(function* (\n lines: ReadonlyArray<string>,\n) {\n const summaryPath = yield* Config.string(\"GITHUB_STEP_SUMMARY\").pipe(Config.withDefault(\"\"));\n if (summaryPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(summaryPath, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n});\n\nconst outcomeOutputs = (\n outcome: ReviewRunOutcome,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<readonly [string, string]> => [\n [\"skipped\", \"false\"],\n [\"conclusion\", conclusion],\n [\"verdict\", outcome.review.verdict],\n [\"input-coverage\", outcome.inputCoverage.status],\n [\"review-assurance\", outcome.assurance.status],\n [\"review-mode\", outcome.reviewMode ?? \"full\"],\n [\"review-reason\", outcome.reviewReason ?? \"direct full review\"],\n [\"inline-comments\", String(outcome.plan.comments.length)],\n [\"demoted-findings\", String(outcome.plan.demoted.length)],\n [\"concerns\", String(outcome.review.concerns?.length ?? 0)],\n [\"unreviewed-paths\", String(outcome.unreviewedPaths.length)],\n ...(outcome.usage === undefined\n ? []\n : ([\n [\"input-tokens\", String(outcome.usage.inputTokens)],\n [\"output-tokens\", String(outcome.usage.outputTokens)],\n ] as const)),\n [\"review-url\", outcome.published?.url ?? \"\"],\n];\n\nconst outcomeSummary = (\n outcome: ReviewRunOutcome,\n modelLabel: string | undefined,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<string> => {\n const scope = splitCarriedScope(outcome);\n return [\n \"### Pull-request review\",\n `- Check conclusion: **${conclusion}**`,\n `- Verdict: **${outcome.review.verdict}**`,\n `- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? \"full\"}`,\n `- Review assurance: **${outcome.assurance.status}** · general discovery ${outcome.assurance.completedGeneralDiscoveryPasses}/${outcome.assurance.requiredGeneralDiscoveryPasses} · specialist ${outcome.assurance.completedSpecialistPasses}/${outcome.assurance.requiredSpecialistPasses} · verification ${outcome.assurance.completedVerificationPasses}/${outcome.assurance.requiredVerificationPasses}`,\n `- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,\n ...(scope.retryablePaths.length === 0\n ? []\n : [\n `- Carried forward: ${scope.retryablePaths.length} unreviewed path(s) retried automatically on the next run (reviewer-side gap, not a code defect)`,\n ]),\n ...(scope.undiffablePaths.length === 0\n ? []\n : [\n `- Unreviewable: ${scope.undiffablePaths.length} path(s) with no reviewable diff (binary or oversized) — remove them from the pull request or exclude them with ignore globs`,\n ]),\n ...(modelLabel === undefined ? [] : [`- Model: \\`${modelLabel}\\``]),\n ...(outcome.usage === undefined\n ? []\n : [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out`]),\n ...(outcome.published === undefined\n ? [\"- Dry run: nothing posted\"]\n : [`- Posted: ${outcome.published.url}`]),\n ];\n};\n\n/** The result of one action invocation: a typed skip or a settled review. */\nexport type ReviewActionResult =\n | { readonly _tag: \"Skipped\"; readonly reason: string }\n | { readonly _tag: \"Completed\"; readonly outcome: ReviewRunOutcome };\n\nconst skip = (reason: string) =>\n Effect.gen(function* () {\n yield* Console.log(`Skipping review: ${reason}`);\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", reason],\n ]);\n yield* writeStepSummary([\"### Pull-request review skipped\", `- Reason: ${reason}`]);\n return { _tag: \"Skipped\", reason } satisfies ReviewActionResult;\n });\n\n/** The Actions run URL for the review footer, or undefined outside Actions. */\nconst resolveRunUrl = Effect.fn(\"resolveRunUrl\")(function* () {\n const runId = yield* Config.string(\"GITHUB_RUN_ID\").pipe(Config.withDefault(\"\"));\n const repository = yield* Config.string(\"GITHUB_REPOSITORY\").pipe(Config.withDefault(\"\"));\n if (runId === \"\" || repository === \"\") return undefined;\n const server = yield* Config.string(\"GITHUB_SERVER_URL\").pipe(\n Config.withDefault(\"https://github.com\"),\n );\n return `${server}/${repository}/actions/runs/${runId}`;\n});\n\n/**\n * The reviewer surface the action harness drives. `PrReview.make` and\n * `PrReview.makeFanOut` provide the state-selection effects. A fingerprint is\n * skip authority only when a profile fingerprint and authenticated review\n * state bind it to settled assurance.\n */\ninterface HarnessedReviewerBase<E, R> {\n /** The action composition root always supplies the selected run context. */\n readonly run: (\n runOptions?: RunReviewOptions,\n ) => Effect.Effect<ReviewRunOutcome, E, R | ReviewExecutionContext>;\n}\n\nexport type HarnessedReviewer<E, R, FingerprintE, FingerprintR> = HarnessedReviewerBase<E, R> &\n (\n | {\n readonly fingerprint?: undefined;\n readonly profileFingerprint?: undefined;\n readonly snapshot?: undefined;\n readonly filterFiles?: undefined;\n }\n | {\n /** Current effective changeset fingerprint; not standalone skip authority. */\n readonly fingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n readonly profileFingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n readonly snapshot: Effect.Effect<\n {\n readonly metadata: PullRequestMetadata;\n readonly files: ReadonlyArray<ChangedFile>;\n },\n FingerprintE,\n FingerprintR\n >;\n readonly filterFiles?:\n | ((files: ReadonlyArray<ChangedFile>) => ReadonlyArray<ChangedFile>)\n | undefined;\n }\n );\n\nconst blockingReasons = (input: {\n readonly findings: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n readonly concerns: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n}): ReadonlyArray<string> => [\n ...input.findings\n .filter((finding) => finding.severity === \"blocking\")\n .map((finding) => `blocking finding: ${finding.title}`),\n ...input.concerns\n .filter((concern) => concern.severity === \"blocking\")\n .map((concern) => `blocking concern: ${concern.title}`),\n];\n\n/**\n * Host-derived check conclusion; model verdict prose cannot weaken it.\n *\n * Blocking code findings outrank machinery gaps — they are the actionable\n * signal. A retryable machinery gap (failed passes, capacity overflow)\n * concludes `incomplete` with reasons that explicitly say the failure is\n * reviewer-side uncertainty carried forward for retry, never an invitation to\n * change code. Undiffable files are the deliberate exception: no retry can\n * settle them, so their reason instructs removal or ignore globs instead of\n * promising an automatic retry. The flat reviewer's constant `unverified`\n * assurance is not a gap.\n */\nexport const concludeReviewOutcome = (\n outcome: ReviewRunOutcome,\n): {\n readonly conclusion: ReviewCheckConclusion;\n readonly reasons: ReadonlyArray<string>;\n} => {\n const machinery: Array<string> = [];\n if (outcome.inputCoverage.status === \"incomplete\" || outcome.assurance.status === \"incomplete\") {\n const scope = splitCarriedScope(outcome);\n if (scope.retryableGap) {\n machinery.push(\n scope.retryablePaths.length > 0\n ? `review infrastructure did not settle — a reviewer-side gap, not a code defect; ${scope.retryablePaths.length} path(s) are carried forward and retried automatically on the next run`\n : \"review infrastructure did not settle — a reviewer-side gap, not a code defect\",\n );\n }\n if (scope.undiffablePaths.length > 0) {\n machinery.push(\n `${scope.undiffablePaths.length} path(s) have no reviewable diff (binary or oversized) and no retry can settle them — remove them from the pull request or exclude them with ignore globs`,\n );\n }\n if (outcome.inputCoverage.status === \"incomplete\") {\n machinery.push(...outcome.inputCoverage.reasons);\n }\n if (outcome.assurance.status === \"incomplete\") {\n machinery.push(...outcome.assurance.reasons);\n }\n }\n const blocking = blockingReasons({\n findings: outcome.activeFindings,\n concerns: outcome.activeConcerns,\n });\n if (blocking.length > 0) {\n return { conclusion: \"blocking\", reasons: [...blocking, ...machinery] };\n }\n if (machinery.length > 0) return { conclusion: \"incomplete\", reasons: machinery };\n return { conclusion: \"success\", reasons: [] };\n};\n\nconst concludeReviewState = (state: ReviewState, adjudicated: ReadonlySet<string>) => {\n const reasons = blockingReasons({\n findings: state.unresolvedFindings.filter(\n (finding) => !adjudicated.has(findingIdentity(finding)),\n ),\n concerns: state.unresolvedConcerns.filter(\n (concern) => !adjudicated.has(concernIdentity(concern)),\n ),\n });\n return reasons.length > 0\n ? ({ conclusion: \"blocking\", reasons } as const)\n : ({ conclusion: \"success\", reasons: [] } as const);\n};\n\nconst skipCoveredReview = Effect.fn(\"skipCoveredReview\")(function* (input: {\n readonly repository: string;\n readonly pullRequestNumber: number;\n readonly reason: string;\n readonly state: ReviewState;\n}) {\n // A maintainer adjudication must lift a preserved blocking conclusion\n // without a code push, so the skip path re-reads adjudications (fail-open;\n // stored ones survive a listing fault) before enforcing the stored state.\n const adjudications = yield* collectReviewAdjudications(input.state.adjudications ?? []);\n const result = concludeReviewState(input.state, new Set(adjudications.map(adjudicationIdentity)));\n yield* Console.log(\n `Skipping review of ${input.repository}#${input.pullRequestNumber}: ${input.reason}.`,\n );\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", input.reason],\n [\"conclusion\", result.conclusion],\n [\"input-coverage\", \"complete\"],\n [\"review-assurance\", \"settled\"],\n [\"review-mode\", \"incremental\"],\n ]);\n yield* writeStepSummary([\n \"### Pull-request review skipped\",\n `- Reason: ${input.reason}`,\n `- Preserved check conclusion: **${result.conclusion}**`,\n ]);\n if (result.conclusion === \"blocking\") {\n return yield* ReviewGateFailed.make({\n conclusion: \"blocking\",\n reasons: result.reasons,\n });\n }\n return { _tag: \"Skipped\", reason: input.reason } satisfies ReviewActionResult;\n});\n\n/**\n * Harness one already-built reviewer inside the Actions environment: resolve\n * the target from the event, provide the GitHub source/publisher/prior\n * reviews, validate/select bounded continuity scope, write step outputs, and apply\n * the host-derived coverage/blocker gate. Draft and non-PR skips are values;\n * an unchanged reviewed head preserves and enforces its stored conclusion. The reviewer's\n * remaining requirements — its model client, any extra tool handlers — stay\n * visible in `R` for the caller.\n */\nexport const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never>(\n reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>,\n options: {\n readonly post?: boolean | undefined;\n /** Skip model execution when the current head already has complete stored coverage. */\n readonly skipUnchanged?: boolean | undefined;\n /** Incremental by default; `final` deliberately re-reviews the full PR diff. */\n readonly reviewMode?: ReviewMode | undefined;\n /** Model binding descriptor for the Actions step summary. */\n readonly modelLabel?: string | undefined;\n /** Explicit test/custom-host history override; GitHub owns the default adapter. */\n readonly priorReviews?: PriorReviews[\"Service\"] | undefined;\n /** Retire marker-bearing prior reviews after a successful post (default true). */\n readonly retireStaleReviews?: boolean | undefined;\n /**\n * Maintain one sticky \"review in progress\" issue comment, updated in\n * place when the run settles. Default false here for custom-harness\n * compatibility; the packaged action enables it by default. Dry runs\n * (`post: false`) never post progress.\n */\n readonly progressComment?: boolean | undefined;\n } = {},\n) =>\n Effect.gen(function* () {\n const event = yield* readGitHubEvent();\n if (Option.isSome(event)) {\n if (event.value.pull_request === undefined) {\n return yield* skip(\"the triggering event carries no pull request\");\n }\n if (event.value.pull_request.draft === true) {\n return yield* skip(\"the pull request is a draft\");\n }\n }\n const target = yield* resolveReviewTarget({});\n // One layer build for state selection AND the run, so both observe\n // the same cached pull-request snapshot.\n return yield* Effect.gen(function* () {\n let selection: ReturnType<typeof selectReviewRange> | undefined;\n if (reviewer.profileFingerprint !== undefined) {\n const [snapshot, profileFingerprint, currentFingerprint] = yield* Effect.all([\n reviewer.snapshot,\n reviewer.profileFingerprint,\n reviewer.fingerprint.pipe(\n Effect.map((fingerprint): string | undefined => fingerprint),\n Effect.orElseSucceed(() => undefined),\n ),\n ]);\n const { metadata, files: fullFiles } = snapshot;\n const history = options.priorReviews ?? (yield* PriorReviews);\n const stateAuthenticator = yield* ReviewStateAuthenticator;\n const recovered =\n stateAuthenticator.status === \"unavailable\"\n ? {\n state: undefined,\n failure:\n stateAuthenticator.unavailableReason ??\n \"an authenticated review-state secret is not configured\",\n }\n : yield* history.latestState.pipe(\n Effect.match({\n onFailure: (failure) => ({ state: undefined, failure: failure.reason }),\n onSuccess: (state) => ({\n state: Option.getOrUndefined(state),\n failure: undefined,\n }),\n }),\n );\n const equivalentPatchState =\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n options.skipUnchanged !== false &&\n recovered.state !== undefined &&\n // Only a fully settled run may be skipped over: an unsettled state\n // carries retryable scope the next run must actually retry.\n recovered.state.settled &&\n currentFingerprint !== undefined &&\n validateReviewState(recovered.state, metadata, profileFingerprint) === undefined &&\n recovered.state.settledScopeFingerprint === currentFingerprint\n ? recovered.state\n : undefined;\n if (equivalentPatchState !== undefined) {\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason:\n equivalentPatchState.reviewedHeadSha === metadata.headSha\n ? \"the current head already has settled stored review assurance\"\n : \"the effective pull-request patch is unchanged since the last settled review\",\n state: equivalentPatchState,\n });\n }\n let comparison: ReviewHeadComparison | undefined;\n let baseComparison: ReviewHeadComparison | undefined;\n let contentComparison: ReviewHeadComparison | undefined;\n if (\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n recovered.state !== undefined\n ) {\n if (recovered.state.reviewedHeadSha === metadata.headSha) {\n comparison = ReviewHeadComparison.make({\n status: \"identical\",\n baseSha: metadata.headSha,\n headSha: metadata.headSha,\n mergeBaseSha: metadata.headSha,\n files: [],\n truncated: false,\n });\n } else {\n comparison = yield* history\n .compareHeads(recovered.state.reviewedHeadSha, metadata.headSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (comparison !== undefined && reviewer.filterFiles !== undefined) {\n comparison = ReviewHeadComparison.make({\n ...comparison,\n files: reviewer.filterFiles(comparison.files),\n });\n }\n }\n if (metadata.baseSha !== undefined && recovered.state.baseSha !== metadata.baseSha) {\n baseComparison = yield* history\n .compareHeads(recovered.state.baseSha, metadata.baseSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (baseComparison !== undefined && reviewer.filterFiles !== undefined) {\n baseComparison = ReviewHeadComparison.make({\n ...baseComparison,\n files: reviewer.filterFiles(baseComparison.files),\n });\n }\n }\n if (\n recovered.state.reviewedHeadSha !== metadata.headSha &&\n (comparison === undefined ||\n !isLineageAncestor(comparison, recovered.state, metadata.headSha))\n ) {\n contentComparison = yield* history\n .compareTrees(recovered.state.reviewedHeadSha, metadata.headSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (contentComparison !== undefined && reviewer.filterFiles !== undefined) {\n contentComparison = ReviewHeadComparison.make({\n ...contentComparison,\n files: reviewer.filterFiles(contentComparison.files),\n });\n }\n }\n }\n selection = {\n ...selectReviewRange({\n requestedMode: options.reviewMode ?? \"incremental\",\n current: metadata,\n fullFiles,\n profileFingerprint,\n priorState: recovered.state,\n comparison,\n baseComparison,\n contentComparison,\n lookupFailure: recovered.failure,\n }),\n stateAuthenticator,\n };\n if (\n options.skipUnchanged !== false &&\n selection.mode === \"incremental\" &&\n selection.files.length === 0 &&\n selection.priorState !== undefined &&\n selection.priorState.settled\n ) {\n const reason = \"no changed review scope since the last settled review head\";\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason,\n state: selection.priorState,\n });\n }\n }\n const executionContext =\n selection ??\n (yield* Effect.gen(function* () {\n const source = yield* PullRequestSource;\n const [metadata, files] = yield* Effect.all([source.metadata, source.changedFiles]);\n return fullReviewSelection({\n reason: \"explicit custom-reviewer full review without continuity selection\",\n files,\n totalFiles: metadata.totalChangedFiles,\n });\n }));\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} (${options.post === false ? \"dry run\" : \"posting\"})...`,\n );\n const runUrl = yield* resolveRunUrl();\n // Progress is a cosmetic, fail-open narration surface: it says a run is\n // working the moment execution starts and is overwritten in place when\n // the run settles. It never gates or delays the review itself.\n const progress =\n options.progressComment === true && options.post !== false\n ? Option.some(yield* ReviewProgressReporter)\n : Option.none<ReviewProgressReporter[\"Service\"]>();\n if (Option.isSome(progress)) {\n const source = yield* PullRequestSource;\n const headMetadata = yield* source.metadata.pipe(Effect.orElseSucceed(() => undefined));\n yield* progress.value.begin({\n headSha: headMetadata?.headSha,\n reviewMode: executionContext.mode,\n reviewReason: executionContext.reason,\n filesInScope: executionContext.files.length,\n modelLabel: options.modelLabel,\n runUrl,\n });\n }\n const runReview = reviewer.run({ post: options.post ?? true, runUrl });\n const reviewEffect = Option.isSome(progress)\n ? runReview.pipe(\n Effect.tapCause(() =>\n progress.value.settle({\n outcome: \"failed\",\n runUrl,\n modelLabel: options.modelLabel,\n }),\n ),\n )\n : runReview;\n const executionLayer = Layer.merge(\n Layer.succeed(ReviewExecutionContext)(executionContext),\n selectedPullRequestSourceLayer(executionContext),\n );\n const outcome = yield* reviewEffect.pipe(Effect.provide(executionLayer));\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n if (options.retireStaleReviews !== false && outcome.state !== undefined) {\n if (outcome.published.authorNodeId === null || outcome.published.submittedAt === null) {\n yield* Console.warn(\n \"Skipping stale-review retirement because GitHub did not return the posted review's actor and submission time.\",\n );\n } else {\n const report = yield* retireStaleReviews({\n currentReviewId: outcome.published.reviewId,\n currentReviewUrl: outcome.published.url,\n currentAuthorNodeId: outcome.published.authorNodeId,\n currentSubmittedAt: outcome.published.submittedAt,\n currentState: outcome.state,\n });\n yield* Console.log(\n `Review retirement: ${report.reviewsRetired} prior review(s), ` +\n `${report.findingsResolved} resolved finding(s), ` +\n `${report.commentsMinimized} minimized inline comment(s), ` +\n `${report.failures} failure(s).`,\n );\n }\n }\n }\n const check = concludeReviewOutcome(outcome);\n if (Option.isSome(progress)) {\n yield* progress.value.settle({\n outcome: \"reviewed\",\n conclusion: check.conclusion,\n verdict: outcome.review.verdict,\n inlineComments: outcome.plan.comments.length,\n reviewUrl: outcome.published?.url,\n runUrl,\n modelLabel: options.modelLabel,\n });\n }\n yield* writeActionOutputs(outcomeOutputs(outcome, check.conclusion));\n yield* writeStepSummary(outcomeSummary(outcome, options.modelLabel, check.conclusion));\n if (check.conclusion !== \"success\") {\n return yield* ReviewGateFailed.make({\n conclusion: check.conclusion,\n reasons: check.reasons,\n });\n }\n return { _tag: \"Completed\", outcome } satisfies ReviewActionResult;\n }).pipe(Effect.provide(gitHubReviewLayers(target)));\n });\n\n/**\n * The packaged, environment-driven action program: inputs from PR_REVIEW_*,\n * reviewer built from the packaged factory, provider client from the\n * matching credential environment variable.\n */\nexport const reviewActionProgram = Effect.gen(function* () {\n const inputs = yield* resolveActionInputs();\n const stateSecret = Option.map(\n yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_STATE_SECRET\")),\n Redacted.make,\n );\n const stateAuthenticatorLayer = Option.match(stateSecret, {\n onNone: () =>\n unavailableReviewStateAuthenticatorLayer(\n \"an authenticated review-state secret is not configured\",\n ),\n onSome: webCryptoReviewStateAuthenticatorLayer,\n });\n const guidance = yield* resolveGuidance(inputs);\n const modelLabel = describeReviewModel(inputs.provider, inputs.model, inputs.effort);\n const defaults = inputs.fanOut ? fanOutReviewBudgetLimits : reviewBudgetLimits;\n const budget =\n inputs.maxDurationMinutes === undefined\n ? defaults\n : UsageBudgetLimits.make({\n ...defaults,\n maxDurationMillis: inputs.maxDurationMinutes * 60_000,\n });\n const shared = {\n guidance,\n ignore: inputs.ignore,\n maxFindings: inputs.maxFindings,\n applyVerdict: inputs.applyVerdict,\n modelLabel,\n budget,\n };\n const harness = {\n post: inputs.post,\n skipUnchanged: inputs.skipUnchanged,\n reviewMode: inputs.reviewMode,\n retireStaleReviews: inputs.retireStaleReviews,\n progressComment: inputs.progressComment,\n modelLabel,\n };\n if (inputs.provider === \"anthropic\") {\n const model = makeAnthropicReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, anthropicClientLayer)),\n );\n }\n const model = makeOpenAiReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, openAiClientLayer)),\n );\n});\n\n/** Run the packaged action program on Node; the bundled action entrypoint. */\nexport const main = (): void =>\n NodeRuntime.runMain(\n reviewActionProgram.pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(\n Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer, compactReviewLoggingLayer),\n ),\n ),\n { disableErrorReporting: true },\n );\n\n/** The packaged GitHub Actions surface. */\nexport const PrReviewAction = {\n inputs: resolveActionInputs,\n run: runReviewAction,\n program: reviewActionProgram,\n main,\n} as const;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"action.mjs","names":[],"sources":["../src/action.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from \"effect\";\nimport { BudgetExceeded, UsageBudgetLimits } from \"effect-agent\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { collectReviewAdjudications } from \"./internal/adjudication.ts\";\nimport { splitCarriedScope } from \"./internal/coverage.ts\";\nimport type { ChangedFile } from \"./internal/diff.ts\";\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { readGitHubEvent, resolveReviewTarget, gitHubReviewLayers } from \"./internal/github-env.ts\";\nimport { PriorReviews } from \"./internal/github.ts\";\nimport { compactReviewLoggingLayer } from \"./internal/logging.ts\";\nimport { ReviewProgressReporter } from \"./internal/progress.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n validateReviewServiceTier,\n type OpenAiServiceTier,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { retireStaleReviews } from \"./internal/retirement.ts\";\nimport {\n adjudicationIdentity,\n concernIdentity,\n findingIdentity,\n fullReviewSelection,\n ReviewExecutionContext,\n ReviewHeadComparison,\n ReviewStateAuthenticator,\n type ReviewTreeComparison,\n isLineageAncestor,\n type ReviewMode,\n type ReviewState,\n selectReviewRange,\n selectedPullRequestSourceLayer,\n unavailableReviewStateAuthenticatorLayer,\n validateReviewState,\n webCryptoReviewStateAuthenticatorLayer,\n} from \"./internal/review-state.ts\";\nimport { fanOutReviewBudgetLimits, reviewBudgetLimits } from \"./internal/run.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\nimport {\n normalizeRepoRelativePath,\n PullRequestSource,\n type PullRequestMetadata,\n} from \"./internal/source.ts\";\n\n// ---------------------------------------------------------------------------\n// The GitHub Actions entrypoint (deployment class E: one bounded ephemeral\n// run, no exactly-once posting). Bounded continuity state travels in GitHub\n// review bodies and is validated before reuse. Inputs arrive as\n// PR_REVIEW_* environment variables set by the action manifest; the target\n// pull request comes from the standard Actions event environment. A non-PR\n// or draft event is a typed skip — green job, nothing posted. Publication\n// happens only after the run settles, so a failed or truncated run posts\n// nothing.\n// ---------------------------------------------------------------------------\n\nexport const ReviewCheckConclusion = Schema.Literals([\"success\", \"blocking\", \"incomplete\"]);\nexport type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;\n\n/** The host-derived blocking or incomplete conclusion failed the check. */\nexport class ReviewGateFailed extends Schema.TaggedError<ReviewGateFailed>()(\"ReviewGateFailed\", {\n conclusion: Schema.Literals([\"blocking\", \"incomplete\"]),\n reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1_000))).check(\n Schema.isMinLength(1),\n ),\n}) {\n override get message() {\n return `Review check concluded '${this.conclusion}': ${this.reasons.join(\"; \")}`;\n }\n}\n\n/** A configured max-duration that cannot bound a run; configuration faults fail loudly. */\nexport class InvalidMaxDurationInput extends Schema.TaggedError<InvalidMaxDurationInput>()(\n \"InvalidMaxDurationInput\",\n {\n minutes: Schema.Int,\n },\n) {\n override get message() {\n return `Invalid max-duration-minutes '${this.minutes}': expected a positive number of minutes.`;\n }\n}\n\n/** Everything the packaged action reads from its environment. */\nexport interface ResolvedActionInputs {\n readonly provider: ReviewProvider;\n readonly model: string | undefined;\n readonly effort: EffortPosition | undefined;\n readonly serviceTier: OpenAiServiceTier | undefined;\n readonly post: boolean;\n readonly applyVerdict: boolean;\n readonly fanOut: boolean;\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n readonly ignore: ReadonlyArray<string>;\n readonly maxFindings: number | undefined;\n readonly maxDurationMinutes: number | undefined;\n readonly reviewMode: ReviewMode;\n readonly skipUnchanged: boolean;\n readonly retireStaleReviews: boolean;\n readonly progressComment: boolean;\n}\n\n/** Read the PR_REVIEW_* input surface (all optional, all defaulted). */\nexport const resolveActionInputs = Effect.fn(\"resolveActionInputs\")(function* () {\n const provider = yield* Config.literals([\"openai\", \"anthropic\"], \"PR_REVIEW_PROVIDER\").pipe(\n Config.withDefault<ReviewProvider>(DEFAULT_PROVIDER),\n );\n const model = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_MODEL\"));\n const effortRaw = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_EFFORT\"));\n let effort: EffortPosition | undefined;\n if (Option.isSome(effortRaw)) {\n effort = parseEffortPosition(effortRaw.value);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw.value });\n }\n }\n const serviceTier = yield* validateReviewServiceTier(\n provider,\n Option.getOrUndefined(\n yield* Config.option(Config.literals([\"fast\"], \"PR_REVIEW_SERVICE_TIER\")),\n ),\n );\n const maxDurationMinutes = Option.getOrUndefined(\n yield* Config.option(Config.int(\"PR_REVIEW_MAX_DURATION_MINUTES\")),\n );\n if (maxDurationMinutes !== undefined && maxDurationMinutes <= 0) {\n return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });\n }\n const post = yield* Config.boolean(\"PR_REVIEW_POST\").pipe(Config.withDefault(true));\n const applyVerdict = yield* Config.boolean(\"PR_REVIEW_APPLY_VERDICT\").pipe(\n Config.withDefault(false),\n );\n const fanOut = yield* Config.boolean(\"PR_REVIEW_FAN_OUT\").pipe(Config.withDefault(true));\n const guidance = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE\"));\n const guidanceFile = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE_FILE\"));\n const ignoreRaw = yield* Config.string(\"PR_REVIEW_IGNORE\").pipe(Config.withDefault(\"\"));\n const maxFindings = yield* Config.option(Config.int(\"PR_REVIEW_MAX_FINDINGS\"));\n const reviewMode = yield* Config.literals([\"incremental\", \"final\"], \"PR_REVIEW_MODE\").pipe(\n Config.withDefault<ReviewMode>(\"incremental\"),\n );\n const skipUnchanged = yield* Config.boolean(\"PR_REVIEW_SKIP_UNCHANGED\").pipe(\n Config.withDefault(true),\n );\n const retireStaleReviews = yield* Config.boolean(\"PR_REVIEW_RETIRE_STALE_REVIEWS\").pipe(\n Config.withDefault(true),\n );\n const progressComment = yield* Config.boolean(\"PR_REVIEW_PROGRESS_COMMENT\").pipe(\n Config.withDefault(true),\n );\n return {\n provider,\n model: Option.getOrUndefined(model),\n effort,\n serviceTier,\n post,\n applyVerdict,\n fanOut,\n guidance: Option.getOrUndefined(guidance),\n guidanceFile: Option.getOrUndefined(guidanceFile),\n ignore: ignoreRaw\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(maxFindings),\n maxDurationMinutes,\n reviewMode,\n skipUnchanged,\n retireStaleReviews,\n progressComment,\n } satisfies ResolvedActionInputs;\n});\n\n/** A guidance file larger than this is refused, never silently truncated. */\nexport const MAX_GUIDANCE_FILE_CHARS = 20_000;\n\n/** A configured guidance file could not be used; configuration faults fail loudly. */\nexport class GuidanceFileUnreadable extends Schema.TaggedError<GuidanceFileUnreadable>()(\n \"GuidanceFileUnreadable\",\n {\n path: Schema.String,\n reason: Schema.String,\n },\n) {\n override get message() {\n return `Cannot use guidance file '${this.path}': ${this.reason}`;\n }\n}\n\n/**\n * Resolve the effective review guidance: the committed guidance file (a\n * repository-owned review profile) first, with any inline guidance appended.\n * A configured-but-unreadable file fails typed — a review silently running\n * without its profile would be worse than a red job.\n */\nexport const resolveGuidance = Effect.fn(\"resolveGuidance\")(function* (inputs: {\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n}) {\n const filePath = inputs.guidanceFile;\n if (filePath === undefined) return inputs.guidance;\n // The profile path is operator configuration, but it stays fail-closed\n // like every other path in this package: workspace-relative only, and the\n // size bound is checked before the file is read into memory.\n const relative = yield* normalizeRepoRelativePath(filePath).pipe(\n Effect.mapError((violation) =>\n GuidanceFileUnreadable.make({ path: filePath, reason: violation.reason }),\n ),\n );\n const fs = yield* FileSystem.FileSystem;\n const unreadable = (error: { readonly _tag: string; readonly message: string }) =>\n GuidanceFileUnreadable.make({\n path: relative,\n reason: `${error._tag}: ${error.message}`.slice(0, 2_048),\n });\n const stat = yield* fs.stat(relative).pipe(Effect.mapError(unreadable));\n const oversized = GuidanceFileUnreadable.make({\n path: relative,\n reason: `File is larger than the ${MAX_GUIDANCE_FILE_CHARS}-character guidance bound.`,\n });\n if (stat.size > BigInt(MAX_GUIDANCE_FILE_CHARS) * 4n) {\n return yield* oversized;\n }\n const content = yield* fs.readFileString(relative).pipe(Effect.mapError(unreadable));\n if (content.length > MAX_GUIDANCE_FILE_CHARS) {\n return yield* oversized;\n }\n const combined = [content.trim(), inputs.guidance ?? \"\"]\n .filter((part) => part.length > 0)\n .join(\"\\n\");\n return combined.length > 0 ? combined : undefined;\n});\n\n/** One step-output line; values must be single-line by construction. */\nconst outputLine = (name: string, value: string): string =>\n `${name}=${value.replaceAll(\"\\n\", \" \").slice(0, 1_000)}\\n`;\n\n/** Append step outputs to GITHUB_OUTPUT when present (no-op locally). */\nexport const writeActionOutputs = Effect.fn(\"writeActionOutputs\")(function* (\n entries: ReadonlyArray<readonly [name: string, value: string]>,\n) {\n const outputPath = yield* Config.string(\"GITHUB_OUTPUT\").pipe(Config.withDefault(\"\"));\n if (outputPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(\n outputPath,\n entries.map(([name, value]) => outputLine(name, value)).join(\"\"),\n {\n flag: \"a\",\n },\n );\n});\n\n/** Append markdown to the GITHUB_STEP_SUMMARY report when present (no-op locally). */\nexport const writeStepSummary = Effect.fn(\"writeStepSummary\")(function* (\n lines: ReadonlyArray<string>,\n) {\n const summaryPath = yield* Config.string(\"GITHUB_STEP_SUMMARY\").pipe(Config.withDefault(\"\"));\n if (summaryPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(summaryPath, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n});\n\nconst outcomeOutputs = (\n outcome: ReviewRunOutcome,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<readonly [string, string]> => [\n [\"skipped\", \"false\"],\n [\"conclusion\", conclusion],\n [\"verdict\", outcome.review.verdict],\n [\"input-coverage\", outcome.inputCoverage.status],\n [\"review-assurance\", outcome.assurance.status],\n [\"review-mode\", outcome.reviewMode ?? \"full\"],\n [\"review-reason\", outcome.reviewReason ?? \"direct full review\"],\n [\"inline-comments\", String(outcome.plan.comments.length)],\n [\"demoted-findings\", String(outcome.plan.demoted.length)],\n [\"concerns\", String(outcome.review.concerns?.length ?? 0)],\n [\"unreviewed-paths\", String(outcome.unreviewedPaths.length)],\n ...(outcome.usage === undefined\n ? []\n : ([\n [\"input-tokens\", String(outcome.usage.inputTokens)],\n [\"output-tokens\", String(outcome.usage.outputTokens)],\n ] as const)),\n [\"review-url\", outcome.published?.url ?? \"\"],\n];\n\nconst outcomeSummary = (\n outcome: ReviewRunOutcome,\n modelLabel: string | undefined,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<string> => {\n const scope = splitCarriedScope(outcome);\n return [\n \"### Pull-request review\",\n `- Check conclusion: **${conclusion}**`,\n `- Verdict: **${outcome.review.verdict}**`,\n `- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? \"full\"}`,\n `- Review assurance: **${outcome.assurance.status}** · general discovery ${outcome.assurance.completedGeneralDiscoveryPasses}/${outcome.assurance.requiredGeneralDiscoveryPasses} · specialist ${outcome.assurance.completedSpecialistPasses}/${outcome.assurance.requiredSpecialistPasses} · verification ${outcome.assurance.completedVerificationPasses}/${outcome.assurance.requiredVerificationPasses}`,\n `- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,\n ...(scope.retryablePaths.length === 0\n ? []\n : [\n `- Carried forward: ${scope.retryablePaths.length} unreviewed path(s) retried automatically on the next run (reviewer-side gap, not a code defect)`,\n ]),\n ...(scope.undiffablePaths.length === 0\n ? []\n : [\n `- Unreviewable: ${scope.undiffablePaths.length} path(s) with no reviewable diff (binary or oversized) — remove them from the pull request or exclude them with ignore globs`,\n ]),\n ...(modelLabel === undefined ? [] : [`- Model: \\`${modelLabel}\\``]),\n ...(outcome.usage === undefined\n ? []\n : [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out`]),\n ...(outcome.published === undefined\n ? [\"- Dry run: nothing posted\"]\n : [`- Posted: ${outcome.published.url}`]),\n ];\n};\n\n/** The result of one action invocation: a typed skip or a settled review. */\nexport type ReviewActionResult =\n | { readonly _tag: \"Skipped\"; readonly reason: string }\n | { readonly _tag: \"Completed\"; readonly outcome: ReviewRunOutcome };\n\nconst skip = (reason: string) =>\n Effect.gen(function* () {\n yield* Console.log(`Skipping review: ${reason}`);\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", reason],\n ]);\n yield* writeStepSummary([\"### Pull-request review skipped\", `- Reason: ${reason}`]);\n return { _tag: \"Skipped\", reason } satisfies ReviewActionResult;\n });\n\n/** The Actions run URL for the review footer, or undefined outside Actions. */\nconst resolveRunUrl = Effect.fn(\"resolveRunUrl\")(function* () {\n const runId = yield* Config.string(\"GITHUB_RUN_ID\").pipe(Config.withDefault(\"\"));\n const repository = yield* Config.string(\"GITHUB_REPOSITORY\").pipe(Config.withDefault(\"\"));\n if (runId === \"\" || repository === \"\") return undefined;\n const server = yield* Config.string(\"GITHUB_SERVER_URL\").pipe(\n Config.withDefault(\"https://github.com\"),\n );\n return `${server}/${repository}/actions/runs/${runId}`;\n});\n\n/**\n * The reviewer surface the action harness drives. `PrReview.make` and\n * `PrReview.makeFanOut` provide the state-selection effects. A fingerprint is\n * skip authority only when a profile fingerprint and authenticated review\n * state bind it to settled assurance.\n */\ninterface HarnessedReviewerBase<E, R> {\n /** The action composition root always supplies the selected run context. */\n readonly run: (\n runOptions?: RunReviewOptions,\n ) => Effect.Effect<ReviewRunOutcome, E, R | ReviewExecutionContext>;\n}\n\nexport type HarnessedReviewer<E, R, FingerprintE, FingerprintR> = HarnessedReviewerBase<E, R> &\n (\n | {\n readonly fingerprint?: undefined;\n readonly profileFingerprint?: undefined;\n readonly snapshot?: undefined;\n readonly filterFiles?: undefined;\n }\n | {\n /** Current effective changeset fingerprint; not standalone skip authority. */\n readonly fingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n readonly profileFingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n readonly snapshot: Effect.Effect<\n {\n readonly metadata: PullRequestMetadata;\n readonly files: ReadonlyArray<ChangedFile>;\n },\n FingerprintE,\n FingerprintR\n >;\n readonly filterFiles?:\n | ((files: ReadonlyArray<ChangedFile>) => ReadonlyArray<ChangedFile>)\n | undefined;\n }\n );\n\nconst blockingReasons = (input: {\n readonly findings: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n readonly concerns: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n}): ReadonlyArray<string> => [\n ...input.findings\n .filter((finding) => finding.severity === \"blocking\")\n .map((finding) => `blocking finding: ${finding.title}`),\n ...input.concerns\n .filter((concern) => concern.severity === \"blocking\")\n .map((concern) => `blocking concern: ${concern.title}`),\n];\n\n/**\n * Host-derived check conclusion; model verdict prose cannot weaken it.\n *\n * Blocking code findings outrank machinery gaps — they are the actionable\n * signal. A retryable machinery gap (failed passes, capacity overflow)\n * concludes `incomplete` with reasons that explicitly say the failure is\n * reviewer-side uncertainty carried forward for retry, never an invitation to\n * change code. Undiffable files are the deliberate exception: no retry can\n * settle them, so their reason instructs removal or ignore globs instead of\n * promising an automatic retry. The flat reviewer's constant `unverified`\n * assurance is not a gap.\n */\nexport const concludeReviewOutcome = (\n outcome: ReviewRunOutcome,\n): {\n readonly conclusion: ReviewCheckConclusion;\n readonly reasons: ReadonlyArray<string>;\n} => {\n const machinery: Array<string> = [];\n if (outcome.inputCoverage.status === \"incomplete\" || outcome.assurance.status === \"incomplete\") {\n const scope = splitCarriedScope(outcome);\n if (scope.retryableGap) {\n machinery.push(\n scope.retryablePaths.length > 0\n ? `review infrastructure did not settle — a reviewer-side gap, not a code defect; ${scope.retryablePaths.length} path(s) are carried forward and retried automatically on the next run`\n : \"review infrastructure did not settle — a reviewer-side gap, not a code defect\",\n );\n }\n if (scope.undiffablePaths.length > 0) {\n machinery.push(\n `${scope.undiffablePaths.length} path(s) have no reviewable diff (binary or oversized) and no retry can settle them — remove them from the pull request or exclude them with ignore globs`,\n );\n }\n if (outcome.inputCoverage.status === \"incomplete\") {\n machinery.push(...outcome.inputCoverage.reasons);\n }\n if (outcome.assurance.status === \"incomplete\") {\n machinery.push(...outcome.assurance.reasons);\n }\n }\n const blocking = blockingReasons({\n findings: outcome.activeFindings,\n concerns: outcome.activeConcerns,\n });\n if (blocking.length > 0) {\n return { conclusion: \"blocking\", reasons: [...blocking, ...machinery] };\n }\n if (machinery.length > 0) return { conclusion: \"incomplete\", reasons: machinery };\n return { conclusion: \"success\", reasons: [] };\n};\n\nconst concludeReviewState = (state: ReviewState, adjudicated: ReadonlySet<string>) => {\n const reasons = blockingReasons({\n findings: state.unresolvedFindings.filter(\n (finding) => !adjudicated.has(findingIdentity(finding)),\n ),\n concerns: state.unresolvedConcerns.filter(\n (concern) => !adjudicated.has(concernIdentity(concern)),\n ),\n });\n return reasons.length > 0\n ? ({ conclusion: \"blocking\", reasons } as const)\n : ({ conclusion: \"success\", reasons: [] } as const);\n};\n\nconst skipCoveredReview = Effect.fn(\"skipCoveredReview\")(function* (input: {\n readonly repository: string;\n readonly pullRequestNumber: number;\n readonly reason: string;\n readonly state: ReviewState;\n}) {\n // A maintainer adjudication must lift a preserved blocking conclusion\n // without a code push, so the skip path re-reads adjudications (fail-open;\n // stored ones survive a listing fault) before enforcing the stored state.\n const adjudications = yield* collectReviewAdjudications(input.state.adjudications ?? []);\n const result = concludeReviewState(input.state, new Set(adjudications.map(adjudicationIdentity)));\n yield* Console.log(\n `Skipping review of ${input.repository}#${input.pullRequestNumber}: ${input.reason}.`,\n );\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", input.reason],\n [\"conclusion\", result.conclusion],\n [\"input-coverage\", \"complete\"],\n [\"review-assurance\", \"settled\"],\n [\"review-mode\", \"incremental\"],\n ]);\n yield* writeStepSummary([\n \"### Pull-request review skipped\",\n `- Reason: ${input.reason}`,\n `- Preserved check conclusion: **${result.conclusion}**`,\n ]);\n if (result.conclusion === \"blocking\") {\n return yield* ReviewGateFailed.make({\n conclusion: \"blocking\",\n reasons: result.reasons,\n });\n }\n return { _tag: \"Skipped\", reason: input.reason } satisfies ReviewActionResult;\n});\n\n/**\n * Harness one already-built reviewer inside the Actions environment: resolve\n * the target from the event, provide the GitHub source/publisher/prior\n * reviews, validate/select bounded continuity scope, write step outputs, and apply\n * the host-derived coverage/blocker gate. Draft and non-PR skips are values;\n * an unchanged reviewed head preserves and enforces its stored conclusion. The reviewer's\n * remaining requirements — its model client, any extra tool handlers — stay\n * visible in `R` for the caller.\n */\nexport const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never>(\n reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>,\n options: {\n readonly post?: boolean | undefined;\n /** Skip model execution when the current head already has complete stored coverage. */\n readonly skipUnchanged?: boolean | undefined;\n /** Incremental by default; `final` deliberately re-reviews the full PR diff. */\n readonly reviewMode?: ReviewMode | undefined;\n /** Model binding descriptor for the Actions step summary. */\n readonly modelLabel?: string | undefined;\n /** Explicit test/custom-host history override; GitHub owns the default adapter. */\n readonly priorReviews?: PriorReviews[\"Service\"] | undefined;\n /** Retire marker-bearing prior reviews after a successful post (default true). */\n readonly retireStaleReviews?: boolean | undefined;\n /**\n * Maintain one sticky \"review in progress\" issue comment, updated in\n * place when the run settles. Default false here for custom-harness\n * compatibility; the packaged action enables it by default. Dry runs\n * (`post: false`) never post progress.\n */\n readonly progressComment?: boolean | undefined;\n } = {},\n) =>\n Effect.gen(function* () {\n const event = yield* readGitHubEvent();\n if (Option.isSome(event)) {\n if (event.value.pull_request === undefined) {\n return yield* skip(\"the triggering event carries no pull request\");\n }\n if (event.value.pull_request.draft === true) {\n return yield* skip(\"the pull request is a draft\");\n }\n }\n const target = yield* resolveReviewTarget({});\n // One layer build for state selection AND the run, so both observe\n // the same cached pull-request snapshot.\n return yield* Effect.gen(function* () {\n let selection: ReturnType<typeof selectReviewRange> | undefined;\n if (reviewer.profileFingerprint !== undefined) {\n const [snapshot, profileFingerprint, currentFingerprint] = yield* Effect.all([\n reviewer.snapshot,\n reviewer.profileFingerprint,\n reviewer.fingerprint.pipe(\n Effect.map((fingerprint): string | undefined => fingerprint),\n Effect.orElseSucceed(() => undefined),\n ),\n ]);\n const { metadata, files: fullFiles } = snapshot;\n const history = options.priorReviews ?? (yield* PriorReviews);\n const stateAuthenticator = yield* ReviewStateAuthenticator;\n const recovered =\n stateAuthenticator.status === \"unavailable\"\n ? {\n state: undefined,\n failure:\n stateAuthenticator.unavailableReason ??\n \"an authenticated review-state secret is not configured\",\n }\n : yield* history.latestState.pipe(\n Effect.match({\n onFailure: (failure) => ({ state: undefined, failure: failure.reason }),\n onSuccess: (state) => ({\n state: Option.getOrUndefined(state),\n failure: undefined,\n }),\n }),\n );\n const equivalentPatchState =\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n options.skipUnchanged !== false &&\n recovered.state !== undefined &&\n // Only a fully settled run may be skipped over: an unsettled state\n // carries retryable scope the next run must actually retry.\n recovered.state.settled &&\n currentFingerprint !== undefined &&\n validateReviewState(recovered.state, metadata, profileFingerprint) === undefined &&\n recovered.state.settledScopeFingerprint === currentFingerprint\n ? recovered.state\n : undefined;\n if (equivalentPatchState !== undefined) {\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason:\n equivalentPatchState.reviewedHeadSha === metadata.headSha\n ? \"the current head already has settled stored review assurance\"\n : \"the effective pull-request patch is unchanged since the last settled review\",\n state: equivalentPatchState,\n });\n }\n let comparison: ReviewHeadComparison | undefined;\n let baseComparison: ReviewHeadComparison | undefined;\n let contentComparison: ReviewTreeComparison | undefined;\n let contentComparisonFailure: string | undefined;\n if (\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n recovered.state !== undefined\n ) {\n if (recovered.state.reviewedHeadSha === metadata.headSha) {\n comparison = ReviewHeadComparison.make({\n status: \"identical\",\n baseSha: metadata.headSha,\n headSha: metadata.headSha,\n mergeBaseSha: metadata.headSha,\n files: [],\n truncated: false,\n });\n } else {\n comparison = yield* history\n .compareHeads(recovered.state.reviewedHeadSha, metadata.headSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (comparison !== undefined && reviewer.filterFiles !== undefined) {\n comparison = ReviewHeadComparison.make({\n ...comparison,\n files: reviewer.filterFiles(comparison.files),\n });\n }\n }\n if (metadata.baseSha !== undefined && recovered.state.baseSha !== metadata.baseSha) {\n baseComparison = yield* history\n .compareHeads(recovered.state.baseSha, metadata.baseSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (baseComparison !== undefined && reviewer.filterFiles !== undefined) {\n baseComparison = ReviewHeadComparison.make({\n ...baseComparison,\n files: reviewer.filterFiles(baseComparison.files),\n });\n }\n }\n if (\n recovered.state.reviewedHeadSha !== metadata.headSha &&\n (comparison === undefined ||\n !isLineageAncestor(comparison, recovered.state, metadata.headSha))\n ) {\n const comparisonPaths = new Set(\n fullFiles.flatMap((file) =>\n file.previousPath === undefined ? [file.path] : [file.path, file.previousPath],\n ),\n );\n for (const finding of recovered.state.unresolvedFindings) {\n comparisonPaths.add(finding.path);\n }\n for (const concern of recovered.state.unresolvedConcerns) {\n for (const path of concern.evidencePaths ?? []) comparisonPaths.add(path);\n }\n for (const path of recovered.state.unreviewedPaths) comparisonPaths.add(path);\n const treeResult = yield* history\n .compareTrees(recovered.state.reviewedHeadSha, metadata.headSha, [...comparisonPaths])\n .pipe(\n Effect.match({\n onFailure: (failure) => ({\n comparison: undefined,\n failure: failure.reason,\n }),\n onSuccess: (treeComparison) => ({\n comparison: treeComparison,\n failure: undefined,\n }),\n }),\n );\n contentComparison = treeResult.comparison;\n contentComparisonFailure = treeResult.failure;\n }\n }\n selection = {\n ...selectReviewRange({\n requestedMode: options.reviewMode ?? \"incremental\",\n current: metadata,\n fullFiles,\n profileFingerprint,\n priorState: recovered.state,\n comparison,\n baseComparison,\n contentComparison,\n contentComparisonFailure,\n lookupFailure: recovered.failure,\n }),\n stateAuthenticator,\n };\n if (\n options.skipUnchanged !== false &&\n selection.mode === \"incremental\" &&\n selection.files.length === 0 &&\n selection.priorState !== undefined &&\n selection.priorState.settled\n ) {\n const reason = \"no changed review scope since the last settled review head\";\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason,\n state: selection.priorState,\n });\n }\n }\n const executionContext =\n selection ??\n (yield* Effect.gen(function* () {\n const source = yield* PullRequestSource;\n const [metadata, files] = yield* Effect.all([source.metadata, source.changedFiles]);\n return fullReviewSelection({\n reason: \"explicit custom-reviewer full review without continuity selection\",\n files,\n totalFiles: metadata.totalChangedFiles,\n });\n }));\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} (${options.post === false ? \"dry run\" : \"posting\"})...`,\n );\n const runUrl = yield* resolveRunUrl();\n // Progress is a cosmetic, fail-open narration surface: it says a run is\n // working the moment execution starts and is overwritten in place when\n // the run settles. It never gates or delays the review itself.\n const progress =\n options.progressComment === true && options.post !== false\n ? Option.some(yield* ReviewProgressReporter)\n : Option.none<ReviewProgressReporter[\"Service\"]>();\n if (Option.isSome(progress)) {\n const source = yield* PullRequestSource;\n const headMetadata = yield* source.metadata.pipe(Effect.orElseSucceed(() => undefined));\n yield* progress.value.begin({\n headSha: headMetadata?.headSha,\n reviewMode: executionContext.mode,\n reviewReason: executionContext.reason,\n filesInScope: executionContext.files.length,\n modelLabel: options.modelLabel,\n runUrl,\n });\n }\n const runReview = reviewer.run({ post: options.post ?? true, runUrl });\n const reviewEffect = Option.isSome(progress)\n ? runReview.pipe(\n Effect.tapCause(() =>\n progress.value.settle({\n outcome: \"failed\",\n runUrl,\n modelLabel: options.modelLabel,\n }),\n ),\n )\n : runReview;\n const executionLayer = Layer.merge(\n Layer.succeed(ReviewExecutionContext)(executionContext),\n selectedPullRequestSourceLayer(executionContext),\n );\n const outcome = yield* reviewEffect.pipe(Effect.provide(executionLayer));\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n if (options.retireStaleReviews !== false && outcome.state !== undefined) {\n if (outcome.published.authorNodeId === null || outcome.published.submittedAt === null) {\n yield* Console.warn(\n \"Skipping stale-review retirement because GitHub did not return the posted review's actor and submission time.\",\n );\n } else {\n const report = yield* retireStaleReviews({\n currentReviewId: outcome.published.reviewId,\n currentReviewUrl: outcome.published.url,\n currentAuthorNodeId: outcome.published.authorNodeId,\n currentSubmittedAt: outcome.published.submittedAt,\n currentState: outcome.state,\n });\n yield* Console.log(\n `Review retirement: ${report.reviewsRetired} prior review(s), ` +\n `${report.findingsResolved} resolved finding(s), ` +\n `${report.commentsMinimized} minimized inline comment(s), ` +\n `${report.failures} failure(s).`,\n );\n }\n }\n }\n const check = concludeReviewOutcome(outcome);\n if (Option.isSome(progress)) {\n yield* progress.value.settle({\n outcome: \"reviewed\",\n conclusion: check.conclusion,\n verdict: outcome.review.verdict,\n inlineComments: outcome.plan.comments.length,\n reviewUrl: outcome.published?.url,\n runUrl,\n modelLabel: options.modelLabel,\n });\n }\n yield* writeActionOutputs(outcomeOutputs(outcome, check.conclusion));\n yield* writeStepSummary(outcomeSummary(outcome, options.modelLabel, check.conclusion));\n if (check.conclusion !== \"success\") {\n return yield* ReviewGateFailed.make({\n conclusion: check.conclusion,\n reasons: check.reasons,\n });\n }\n return { _tag: \"Completed\", outcome } satisfies ReviewActionResult;\n }).pipe(Effect.provide(gitHubReviewLayers(target)));\n });\n\n/**\n * The packaged, environment-driven action program: inputs from PR_REVIEW_*,\n * reviewer built from the packaged factory, provider client from the\n * matching credential environment variable.\n */\nexport const reviewActionProgram = Effect.gen(function* () {\n const inputs = yield* resolveActionInputs();\n const stateSecret = Option.map(\n yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_STATE_SECRET\")),\n Redacted.make,\n );\n const stateAuthenticatorLayer = Option.match(stateSecret, {\n onNone: () =>\n unavailableReviewStateAuthenticatorLayer(\n \"an authenticated review-state secret is not configured\",\n ),\n onSome: webCryptoReviewStateAuthenticatorLayer,\n });\n const guidance = yield* resolveGuidance(inputs);\n const modelLabel = describeReviewModel(\n inputs.provider,\n inputs.model,\n inputs.effort,\n inputs.serviceTier,\n );\n const defaults = inputs.fanOut ? fanOutReviewBudgetLimits : reviewBudgetLimits;\n const budget =\n inputs.maxDurationMinutes === undefined\n ? defaults\n : UsageBudgetLimits.make({\n ...defaults,\n maxDurationMillis: inputs.maxDurationMinutes * 60_000,\n });\n const shared = {\n guidance,\n ignore: inputs.ignore,\n maxFindings: inputs.maxFindings,\n applyVerdict: inputs.applyVerdict,\n modelLabel,\n budget,\n };\n const harness = {\n post: inputs.post,\n skipUnchanged: inputs.skipUnchanged,\n reviewMode: inputs.reviewMode,\n retireStaleReviews: inputs.retireStaleReviews,\n progressComment: inputs.progressComment,\n modelLabel,\n };\n if (inputs.provider === \"anthropic\") {\n const model = makeAnthropicReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, anthropicClientLayer)),\n );\n }\n const model = makeOpenAiReviewModel(inputs.model, inputs.effort, inputs.serviceTier);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, openAiClientLayer)),\n );\n});\n\n/** Run the packaged action program on Node; the bundled action entrypoint. */\nexport const main = (): void =>\n NodeRuntime.runMain(\n reviewActionProgram.pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(\n Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer, compactReviewLoggingLayer),\n ),\n ),\n { disableErrorReporting: true },\n );\n\n/** The packaged GitHub Actions surface. */\nexport const PrReviewAction = {\n inputs: resolveActionInputs,\n run: runReviewAction,\n program: reviewActionProgram,\n main,\n} as const;\n"],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { c as fingerprintUnchanged, wn as fullReviewExecutionContextLayer } from "./github-CCuLgyqb.mjs";
|
|
2
|
+
import { D as PrReview, J as parseEffortPosition, K as InvalidEffortInput, R as ReviewPublicationPlan, c as anthropicClientLayer, d as makeOpenAiReviewModel, f as openAiClientLayer, g as gitHubReviewLayers, l as describeReviewModel, n as DEFAULT_PROVIDER, p as validateReviewServiceTier, t as DEFAULT_MODEL, u as makeAnthropicReviewModel, v as resolveReviewTarget } from "./providers-Br9FRn7j.mjs";
|
|
3
3
|
import { Console, Effect, Layer, Option, Schema } from "effect";
|
|
4
4
|
import { BudgetExceeded } from "effect-agent";
|
|
5
5
|
import { FetchHttpClient } from "effect/unstable/http";
|
|
@@ -11,6 +11,7 @@ const prFlag = Flag.integer("pr").pipe(Flag.optional, Flag.withDescription("Pull
|
|
|
11
11
|
const providerFlag = Flag.string("provider").pipe(Flag.withDefault(DEFAULT_PROVIDER), Flag.withDescription("Model provider: \"openai\" (default) or \"anthropic\"."));
|
|
12
12
|
const modelFlag = Flag.string("model").pipe(Flag.optional, Flag.withDescription(`Model id (defaults: openai ${DEFAULT_MODEL.openai}, anthropic ${DEFAULT_MODEL.anthropic}).`));
|
|
13
13
|
const effortFlag = Flag.string("effort").pipe(Flag.optional, Flag.withDescription("Reasoning effort: \"low\", \"medium\", \"high\", \"xhigh\", \"max\", or a number in [0, 1] resolved onto the provider's own ladder."));
|
|
14
|
+
const serviceTierFlag = Flag.choice("service-tier", ["fast"]).pipe(Flag.optional, Flag.withDescription("OpenAI Responses service tier. The only supported value is \"fast\"; omit it to use the OpenAI project default."));
|
|
14
15
|
const postFlag = Flag.boolean("post").pipe(Flag.withDefault(false), Flag.withDescription("Post the review to GitHub; without it the plan prints to stdout."));
|
|
15
16
|
const applyVerdictFlag = Flag.boolean("apply-verdict").pipe(Flag.withDefault(false), Flag.withDescription("Map the model verdict onto APPROVE/REQUEST_CHANGES instead of always COMMENT."));
|
|
16
17
|
const fanOutFlag = Flag.boolean("fan-out").pipe(Flag.withDefault(false), Flag.withDescription("Fan the review out to bounded per-unit subagent reviewers (S1 attached delegation) instead of one flat reviewer."));
|
|
@@ -30,6 +31,7 @@ const command = Command.make("pr-review", {
|
|
|
30
31
|
provider: providerFlag,
|
|
31
32
|
model: modelFlag,
|
|
32
33
|
effort: effortFlag,
|
|
34
|
+
serviceTier: serviceTierFlag,
|
|
33
35
|
post: postFlag,
|
|
34
36
|
applyVerdict: applyVerdictFlag,
|
|
35
37
|
fanOut: fanOutFlag,
|
|
@@ -38,6 +40,7 @@ const command = Command.make("pr-review", {
|
|
|
38
40
|
skipUnchanged: skipUnchangedFlag
|
|
39
41
|
}, (flags) => Effect.gen(function* () {
|
|
40
42
|
const provider = yield* decodeProvider(flags.provider);
|
|
43
|
+
const serviceTier = yield* validateReviewServiceTier(provider, Option.getOrUndefined(flags.serviceTier));
|
|
41
44
|
const target = yield* resolveReviewTarget({
|
|
42
45
|
repository: Option.getOrUndefined(flags.repo),
|
|
43
46
|
number: Option.getOrUndefined(flags.pr)
|
|
@@ -53,7 +56,7 @@ const command = Command.make("pr-review", {
|
|
|
53
56
|
applyVerdict: flags.applyVerdict,
|
|
54
57
|
ignore: flags.ignore.split(",").map((pattern) => pattern.trim()).filter((pattern) => pattern.length > 0),
|
|
55
58
|
maxFindings: Option.getOrUndefined(flags.maxFindings),
|
|
56
|
-
modelLabel: describeReviewModel(provider, model, effort)
|
|
59
|
+
modelLabel: describeReviewModel(provider, model, effort, serviceTier)
|
|
57
60
|
};
|
|
58
61
|
yield* Console.log(`Reviewing ${target.repository}#${target.number} with ${provider}:${model ?? DEFAULT_MODEL[provider]} (${flags.post ? "posting" : "dry run"}${flags.fanOut ? ", fan-out" : ""})...`);
|
|
59
62
|
const githubLayers = gitHubReviewLayers(target);
|
|
@@ -74,7 +77,7 @@ const command = Command.make("pr-review", {
|
|
|
74
77
|
model: boundModel
|
|
75
78
|
})).pipe(Effect.provide(Layer.merge(githubLayers, anthropicClientLayer)));
|
|
76
79
|
} else {
|
|
77
|
-
const boundModel = makeOpenAiReviewModel(model, effort);
|
|
80
|
+
const boundModel = makeOpenAiReviewModel(model, effort, serviceTier);
|
|
78
81
|
result = yield* runOrSkip(flags.fanOut ? PrReview.makeFanOut({
|
|
79
82
|
...shared,
|
|
80
83
|
model: boundModel
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["CliCommand"],"sources":["../src/cli.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Console, Effect, Layer, Option, Schema } from \"effect\";\nimport { BudgetExceeded } from \"effect-agent\";\nimport { Command as CliCommand, Flag } from \"effect/unstable/cli\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { gitHubReviewLayers, resolveReviewTarget } from \"./internal/github-env.ts\";\nimport { fingerprintUnchanged } from \"./internal/github.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_MODEL,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { ReviewPublicationPlan } from \"./internal/render.ts\";\nimport { fullReviewExecutionContextLayer } from \"./internal/review-state.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\n\n// ---------------------------------------------------------------------------\n// The CLI entrypoint: resolve which pull request to review (flags first, then\n// the GitHub Actions event environment), run one bounded ephemeral review,\n// and either print the validated publication plan (default: dry run) or post\n// it as a pull-request review (--post).\n// ---------------------------------------------------------------------------\n\nconst repoFlag = Flag.string(\"repo\").pipe(\n Flag.optional,\n Flag.withDescription(\"Repository as owner/name; defaults to GITHUB_REPOSITORY.\"),\n);\nconst prFlag = Flag.integer(\"pr\").pipe(\n Flag.optional,\n Flag.withDescription(\"Pull request number; defaults to the GITHUB_EVENT_PATH payload.\"),\n);\nconst providerFlag = Flag.string(\"provider\").pipe(\n Flag.withDefault<string>(DEFAULT_PROVIDER),\n Flag.withDescription('Model provider: \"openai\" (default) or \"anthropic\".'),\n);\nconst modelFlag = Flag.string(\"model\").pipe(\n Flag.optional,\n Flag.withDescription(\n `Model id (defaults: openai ${DEFAULT_MODEL.openai}, anthropic ${DEFAULT_MODEL.anthropic}).`,\n ),\n);\nconst effortFlag = Flag.string(\"effort\").pipe(\n Flag.optional,\n Flag.withDescription(\n 'Reasoning effort: \"low\", \"medium\", \"high\", \"xhigh\", \"max\", or a number in [0, 1] resolved onto the provider\\'s own ladder.',\n ),\n);\nconst postFlag = Flag.boolean(\"post\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Post the review to GitHub; without it the plan prints to stdout.\"),\n);\nconst applyVerdictFlag = Flag.boolean(\"apply-verdict\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Map the model verdict onto APPROVE/REQUEST_CHANGES instead of always COMMENT.\",\n ),\n);\nconst fanOutFlag = Flag.boolean(\"fan-out\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Fan the review out to bounded per-unit subagent reviewers (S1 attached delegation) instead of one flat reviewer.\",\n ),\n);\nconst ignoreFlag = Flag.string(\"ignore\").pipe(\n Flag.withDefault(\"\"),\n Flag.withDescription(\n 'Comma-separated glob patterns removed from the review surface, e.g. \"**/*.lock,dist/**\".',\n ),\n);\nconst maxFindingsFlag = Flag.integer(\"max-findings\").pipe(\n Flag.optional,\n Flag.withDescription(\"Findings bound (1-20); the schema cap of 20 applies regardless.\"),\n);\nconst skipUnchangedFlag = Flag.boolean(\"skip-unchanged\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Skip when the changeset fingerprint matches the last posted review (explicit runs review unconditionally by default).\",\n ),\n);\n\n/** An unknown --provider value; the two supported providers are spelled out. */\nclass UnknownProvider extends Schema.TaggedError<UnknownProvider>()(\"UnknownProvider\", {\n provider: Schema.String,\n}) {\n override get message() {\n return `Unknown provider '${this.provider}': expected \"openai\" or \"anthropic\".`;\n }\n}\n\nconst decodeProvider = (raw: string): Effect.Effect<ReviewProvider, UnknownProvider> =>\n raw === \"openai\" || raw === \"anthropic\"\n ? Effect.succeed(raw)\n : Effect.fail(UnknownProvider.make({ provider: raw }));\n\nconst command = CliCommand.make(\n \"pr-review\",\n {\n repo: repoFlag,\n pr: prFlag,\n provider: providerFlag,\n model: modelFlag,\n effort: effortFlag,\n post: postFlag,\n applyVerdict: applyVerdictFlag,\n fanOut: fanOutFlag,\n ignore: ignoreFlag,\n maxFindings: maxFindingsFlag,\n skipUnchanged: skipUnchangedFlag,\n },\n (flags) =>\n Effect.gen(function* () {\n const provider = yield* decodeProvider(flags.provider);\n const target = yield* resolveReviewTarget({\n repository: Option.getOrUndefined(flags.repo),\n number: Option.getOrUndefined(flags.pr),\n });\n const model = Option.getOrUndefined(flags.model);\n const effortRaw = Option.getOrUndefined(flags.effort);\n let effort: EffortPosition | undefined;\n if (effortRaw !== undefined) {\n effort = parseEffortPosition(effortRaw);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw });\n }\n }\n const shared = {\n applyVerdict: flags.applyVerdict,\n ignore: flags.ignore\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(flags.maxFindings),\n modelLabel: describeReviewModel(provider, model, effort),\n };\n\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} with ${provider}:${model ?? DEFAULT_MODEL[provider]} (${flags.post ? \"posting\" : \"dry run\"}${flags.fanOut ? \", fan-out\" : \"\"})...`,\n );\n\n const githubLayers = gitHubReviewLayers(target);\n // Fingerprint check and run under ONE provide, sharing the cached\n // pull-request snapshot; None means \"unchanged, skipped\".\n const runOrSkip = <E, R, FingerprintE, FingerprintR>(reviewer: {\n readonly run: (runOptions?: RunReviewOptions) => Effect.Effect<ReviewRunOutcome, E, R>;\n readonly fingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n }) =>\n Effect.gen(function* () {\n if (flags.skipUnchanged) {\n const current = yield* reviewer.fingerprint;\n if (yield* fingerprintUnchanged(current)) {\n return Option.none<ReviewRunOutcome>();\n }\n }\n return Option.some(\n yield* reviewer\n .run({ post: flags.post })\n .pipe(Effect.provide(fullReviewExecutionContextLayer(\"explicit CLI full review\"))),\n );\n });\n\n let result: Option.Option<ReviewRunOutcome>;\n if (provider === \"anthropic\") {\n const boundModel = makeAnthropicReviewModel(model, effort);\n const reviewer = flags.fanOut\n ? PrReview.makeFanOut({ ...shared, model: boundModel })\n : PrReview.make({ ...shared, model: boundModel });\n result = yield* runOrSkip(reviewer).pipe(\n Effect.provide(Layer.merge(githubLayers, anthropicClientLayer)),\n );\n } else {\n const boundModel = makeOpenAiReviewModel(model, effort);\n const reviewer = flags.fanOut\n ? PrReview.makeFanOut({ ...shared, model: boundModel })\n : PrReview.make({ ...shared, model: boundModel });\n result = yield* runOrSkip(reviewer).pipe(\n Effect.provide(Layer.merge(githubLayers, openAiClientLayer)),\n );\n }\n\n if (Option.isNone(result)) {\n yield* Console.log(\"Skipped: changeset unchanged since the last posted review.\");\n return;\n }\n const outcome = result.value;\n\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n } else {\n const encodedPlan = yield* Schema.encodeEffect(ReviewPublicationPlan)(outcome.plan);\n yield* Console.log(JSON.stringify(encodedPlan, null, 2));\n }\n }),\n).pipe(\n CliCommand.withDescription(\n \"Review one GitHub pull request with an @effect-agent/pr-review reviewer and post the validated result as a pull-request review.\",\n ),\n);\n\nconst program = CliCommand.run(command, { version: \"0.0.0\" }).pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(Layer.merge(NodeServices.layer, FetchHttpClient.layer)),\n);\n\nNodeRuntime.runMain(program, { disableErrorReporting: true });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["CliCommand"],"sources":["../src/cli.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Console, Effect, Layer, Option, Schema } from \"effect\";\nimport { BudgetExceeded } from \"effect-agent\";\nimport { Command as CliCommand, Flag } from \"effect/unstable/cli\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { gitHubReviewLayers, resolveReviewTarget } from \"./internal/github-env.ts\";\nimport { fingerprintUnchanged } from \"./internal/github.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_MODEL,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n validateReviewServiceTier,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { ReviewPublicationPlan } from \"./internal/render.ts\";\nimport { fullReviewExecutionContextLayer } from \"./internal/review-state.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\n\n// ---------------------------------------------------------------------------\n// The CLI entrypoint: resolve which pull request to review (flags first, then\n// the GitHub Actions event environment), run one bounded ephemeral review,\n// and either print the validated publication plan (default: dry run) or post\n// it as a pull-request review (--post).\n// ---------------------------------------------------------------------------\n\nconst repoFlag = Flag.string(\"repo\").pipe(\n Flag.optional,\n Flag.withDescription(\"Repository as owner/name; defaults to GITHUB_REPOSITORY.\"),\n);\nconst prFlag = Flag.integer(\"pr\").pipe(\n Flag.optional,\n Flag.withDescription(\"Pull request number; defaults to the GITHUB_EVENT_PATH payload.\"),\n);\nconst providerFlag = Flag.string(\"provider\").pipe(\n Flag.withDefault<string>(DEFAULT_PROVIDER),\n Flag.withDescription('Model provider: \"openai\" (default) or \"anthropic\".'),\n);\nconst modelFlag = Flag.string(\"model\").pipe(\n Flag.optional,\n Flag.withDescription(\n `Model id (defaults: openai ${DEFAULT_MODEL.openai}, anthropic ${DEFAULT_MODEL.anthropic}).`,\n ),\n);\nconst effortFlag = Flag.string(\"effort\").pipe(\n Flag.optional,\n Flag.withDescription(\n 'Reasoning effort: \"low\", \"medium\", \"high\", \"xhigh\", \"max\", or a number in [0, 1] resolved onto the provider\\'s own ladder.',\n ),\n);\nconst serviceTierFlag = Flag.choice(\"service-tier\", [\"fast\"]).pipe(\n Flag.optional,\n Flag.withDescription(\n 'OpenAI Responses service tier. The only supported value is \"fast\"; omit it to use the OpenAI project default.',\n ),\n);\nconst postFlag = Flag.boolean(\"post\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Post the review to GitHub; without it the plan prints to stdout.\"),\n);\nconst applyVerdictFlag = Flag.boolean(\"apply-verdict\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Map the model verdict onto APPROVE/REQUEST_CHANGES instead of always COMMENT.\",\n ),\n);\nconst fanOutFlag = Flag.boolean(\"fan-out\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Fan the review out to bounded per-unit subagent reviewers (S1 attached delegation) instead of one flat reviewer.\",\n ),\n);\nconst ignoreFlag = Flag.string(\"ignore\").pipe(\n Flag.withDefault(\"\"),\n Flag.withDescription(\n 'Comma-separated glob patterns removed from the review surface, e.g. \"**/*.lock,dist/**\".',\n ),\n);\nconst maxFindingsFlag = Flag.integer(\"max-findings\").pipe(\n Flag.optional,\n Flag.withDescription(\"Findings bound (1-20); the schema cap of 20 applies regardless.\"),\n);\nconst skipUnchangedFlag = Flag.boolean(\"skip-unchanged\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\n \"Skip when the changeset fingerprint matches the last posted review (explicit runs review unconditionally by default).\",\n ),\n);\n\n/** An unknown --provider value; the two supported providers are spelled out. */\nclass UnknownProvider extends Schema.TaggedError<UnknownProvider>()(\"UnknownProvider\", {\n provider: Schema.String,\n}) {\n override get message() {\n return `Unknown provider '${this.provider}': expected \"openai\" or \"anthropic\".`;\n }\n}\n\nconst decodeProvider = (raw: string): Effect.Effect<ReviewProvider, UnknownProvider> =>\n raw === \"openai\" || raw === \"anthropic\"\n ? Effect.succeed(raw)\n : Effect.fail(UnknownProvider.make({ provider: raw }));\n\nconst command = CliCommand.make(\n \"pr-review\",\n {\n repo: repoFlag,\n pr: prFlag,\n provider: providerFlag,\n model: modelFlag,\n effort: effortFlag,\n serviceTier: serviceTierFlag,\n post: postFlag,\n applyVerdict: applyVerdictFlag,\n fanOut: fanOutFlag,\n ignore: ignoreFlag,\n maxFindings: maxFindingsFlag,\n skipUnchanged: skipUnchangedFlag,\n },\n (flags) =>\n Effect.gen(function* () {\n const provider = yield* decodeProvider(flags.provider);\n const serviceTier = yield* validateReviewServiceTier(\n provider,\n Option.getOrUndefined(flags.serviceTier),\n );\n const target = yield* resolveReviewTarget({\n repository: Option.getOrUndefined(flags.repo),\n number: Option.getOrUndefined(flags.pr),\n });\n const model = Option.getOrUndefined(flags.model);\n const effortRaw = Option.getOrUndefined(flags.effort);\n let effort: EffortPosition | undefined;\n if (effortRaw !== undefined) {\n effort = parseEffortPosition(effortRaw);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw });\n }\n }\n const shared = {\n applyVerdict: flags.applyVerdict,\n ignore: flags.ignore\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(flags.maxFindings),\n modelLabel: describeReviewModel(provider, model, effort, serviceTier),\n };\n\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} with ${provider}:${model ?? DEFAULT_MODEL[provider]} (${flags.post ? \"posting\" : \"dry run\"}${flags.fanOut ? \", fan-out\" : \"\"})...`,\n );\n\n const githubLayers = gitHubReviewLayers(target);\n // Fingerprint check and run under ONE provide, sharing the cached\n // pull-request snapshot; None means \"unchanged, skipped\".\n const runOrSkip = <E, R, FingerprintE, FingerprintR>(reviewer: {\n readonly run: (runOptions?: RunReviewOptions) => Effect.Effect<ReviewRunOutcome, E, R>;\n readonly fingerprint: Effect.Effect<string, FingerprintE, FingerprintR>;\n }) =>\n Effect.gen(function* () {\n if (flags.skipUnchanged) {\n const current = yield* reviewer.fingerprint;\n if (yield* fingerprintUnchanged(current)) {\n return Option.none<ReviewRunOutcome>();\n }\n }\n return Option.some(\n yield* reviewer\n .run({ post: flags.post })\n .pipe(Effect.provide(fullReviewExecutionContextLayer(\"explicit CLI full review\"))),\n );\n });\n\n let result: Option.Option<ReviewRunOutcome>;\n if (provider === \"anthropic\") {\n const boundModel = makeAnthropicReviewModel(model, effort);\n const reviewer = flags.fanOut\n ? PrReview.makeFanOut({ ...shared, model: boundModel })\n : PrReview.make({ ...shared, model: boundModel });\n result = yield* runOrSkip(reviewer).pipe(\n Effect.provide(Layer.merge(githubLayers, anthropicClientLayer)),\n );\n } else {\n const boundModel = makeOpenAiReviewModel(model, effort, serviceTier);\n const reviewer = flags.fanOut\n ? PrReview.makeFanOut({ ...shared, model: boundModel })\n : PrReview.make({ ...shared, model: boundModel });\n result = yield* runOrSkip(reviewer).pipe(\n Effect.provide(Layer.merge(githubLayers, openAiClientLayer)),\n );\n }\n\n if (Option.isNone(result)) {\n yield* Console.log(\"Skipped: changeset unchanged since the last posted review.\");\n return;\n }\n const outcome = result.value;\n\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n } else {\n const encodedPlan = yield* Schema.encodeEffect(ReviewPublicationPlan)(outcome.plan);\n yield* Console.log(JSON.stringify(encodedPlan, null, 2));\n }\n }),\n).pipe(\n CliCommand.withDescription(\n \"Review one GitHub pull request with an @effect-agent/pr-review reviewer and post the validated result as a pull-request review.\",\n ),\n);\n\nconst program = CliCommand.run(command, { version: \"0.0.0\" }).pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(Layer.merge(NodeServices.layer, FetchHttpClient.layer)),\n);\n\nNodeRuntime.runMain(program, { disableErrorReporting: true });\n"],"mappings":"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"}
|