@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 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. See its [setup and behavior guide](../../action/README.md)
173
- and [input reference](../../action/action.yml). Custom hosts can import `runReviewAction` from
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 { Br as ChangedFile, Cn as ReviewStateAuthenticator, Fr as PullRequestMetadata, Ir as PullRequestSource, L as PriorReviews, Lr as PullRequestSourceFailure, P as GitHubApiFailure, Q as ReviewRetirementHost, Qt as ReviewAdjudicationHost, gn as ReviewExecutionContext, vn as ReviewMode, z as ReviewPublisher } from "./fan-out-CMEsbFLk.mjs";
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: ReviewProvider;
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: ReviewProvider;
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 { An as validateReviewState, Cn as fullReviewSelection, En as selectedPullRequestSourceLayer, St as splitCarriedScope, Tn as selectReviewRange, Yt as retireStaleReviews, a as PriorReviews, cn as ReviewStateAuthenticator, gn as adjudicationIdentity, jn as webCryptoReviewStateAuthenticatorLayer, jt as collectReviewAdjudications, kn as unavailableReviewStateAuthenticatorLayer, nn as ReviewExecutionContext, rn as ReviewHeadComparison, vn as concernIdentity, vr as PullRequestSource, wn as isLineageAncestor, xr as normalizeRepoRelativePath, yn as findingIdentity } from "./github-NjgxGqwM.mjs";
2
- import { A as reviewBudgetLimits, C as PrReview, H as InvalidEffortInput, W as parseEffortPosition, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, g as ReviewProgressReporter, k as fanOutReviewBudgetLimits, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, p as readGitHubEvent, s as makeAnthropicReviewModel } from "./providers-CODZQCmL.mjs";
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
- contentComparison = yield* history.compareTrees(recovered.state.reviewedHeadSha, metadata.headSha).pipe(Effect.orElseSucceed(() => void 0));
324
- if (contentComparison !== void 0 && reviewer.filterFiles !== void 0) contentComparison = ReviewHeadComparison.make({
325
- ...contentComparison,
326
- files: reviewer.filterFiles(contentComparison.files)
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
@@ -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 { Sn as fullReviewExecutionContextLayer, c as fingerprintUnchanged } from "./github-NjgxGqwM.mjs";
2
- import { C as PrReview, H as InvalidEffortInput, P as ReviewPublicationPlan, W as parseEffortPosition, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, s as makeAnthropicReviewModel, t as DEFAULT_MODEL } from "./providers-CODZQCmL.mjs";
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":";;;;;;;;AAgCA,MAAM,WAAW,KAAK,OAAO,MAAM,CAAC,CAAC,KACnC,KAAK,UACL,KAAK,gBAAgB,0DAA0D,CACjF;AACA,MAAM,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,KAChC,KAAK,UACL,KAAK,gBAAgB,iEAAiE,CACxF;AACA,MAAM,eAAe,KAAK,OAAO,UAAU,CAAC,CAAC,KAC3C,KAAK,YAAoB,gBAAgB,GACzC,KAAK,gBAAgB,wDAAoD,CAC3E;AACA,MAAM,YAAY,KAAK,OAAO,OAAO,CAAC,CAAC,KACrC,KAAK,UACL,KAAK,gBACH,8BAA8B,cAAc,OAAO,cAAc,cAAc,UAAU,GAC3F,CACF;AACA,MAAM,aAAa,KAAK,OAAO,QAAQ,CAAC,CAAC,KACvC,KAAK,UACL,KAAK,gBACH,qIACF,CACF;AACA,MAAM,kBAAkB,KAAK,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,KAC5D,KAAK,UACL,KAAK,gBACH,iHACF,CACF;AACA,MAAM,WAAW,KAAK,QAAQ,MAAM,CAAC,CAAC,KACpC,KAAK,YAAY,KAAK,GACtB,KAAK,gBAAgB,kEAAkE,CACzF;AACA,MAAM,mBAAmB,KAAK,QAAQ,eAAe,CAAC,CAAC,KACrD,KAAK,YAAY,KAAK,GACtB,KAAK,gBACH,+EACF,CACF;AACA,MAAM,aAAa,KAAK,QAAQ,SAAS,CAAC,CAAC,KACzC,KAAK,YAAY,KAAK,GACtB,KAAK,gBACH,kHACF,CACF;AACA,MAAM,aAAa,KAAK,OAAO,QAAQ,CAAC,CAAC,KACvC,KAAK,YAAY,EAAE,GACnB,KAAK,gBACH,4FACF,CACF;AACA,MAAM,kBAAkB,KAAK,QAAQ,cAAc,CAAC,CAAC,KACnD,KAAK,UACL,KAAK,gBAAgB,iEAAiE,CACxF;AACA,MAAM,oBAAoB,KAAK,QAAQ,gBAAgB,CAAC,CAAC,KACvD,KAAK,YAAY,KAAK,GACtB,KAAK,gBACH,uHACF,CACF;;AAGA,IAAM,kBAAN,cAA8B,OAAO,YAA6B,CAAC,CAAC,mBAAmB,EACrF,UAAU,OAAO,OACnB,CAAC,CAAC,CAAC;CACD,IAAa,UAAU;EACrB,OAAO,qBAAqB,KAAK,SAAS;CAC5C;AACF;AAEA,MAAM,kBAAkB,QACtB,QAAQ,YAAY,QAAQ,cACxB,OAAO,QAAQ,GAAG,IAClB,OAAO,KAAK,gBAAgB,KAAK,EAAE,UAAU,IAAI,CAAC,CAAC;AAEzD,MAAM,UAAUA,QAAW,KACzB,aACA;CACE,MAAM;CACN,IAAI;CACJ,UAAU;CACV,OAAO;CACP,QAAQ;CACR,aAAa;CACb,MAAM;CACN,cAAc;CACd,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,eAAe;AACjB,IACC,UACC,OAAO,IAAI,aAAa;CACtB,MAAM,WAAW,OAAO,eAAe,MAAM,QAAQ;CACrD,MAAM,cAAc,OAAO,0BACzB,UACA,OAAO,eAAe,MAAM,WAAW,CACzC;CACA,MAAM,SAAS,OAAO,oBAAoB;EACxC,YAAY,OAAO,eAAe,MAAM,IAAI;EAC5C,QAAQ,OAAO,eAAe,MAAM,EAAE;CACxC,CAAC;CACD,MAAM,QAAQ,OAAO,eAAe,MAAM,KAAK;CAC/C,MAAM,YAAY,OAAO,eAAe,MAAM,MAAM;CACpD,IAAI;CACJ,IAAI,cAAc,KAAA,GAAW;EAC3B,SAAS,oBAAoB,SAAS;EACtC,IAAI,WAAW,KAAA,GACb,OAAO,OAAO,mBAAmB,KAAK,EAAE,OAAO,UAAU,CAAC;CAE9D;CACA,MAAM,SAAS;EACb,cAAc,MAAM;EACpB,QAAQ,MAAM,OACX,MAAM,GAAG,CAAC,CACV,KAAK,YAAY,QAAQ,KAAK,CAAC,CAAC,CAChC,QAAQ,YAAY,QAAQ,SAAS,CAAC;EACzC,aAAa,OAAO,eAAe,MAAM,WAAW;EACpD,YAAY,oBAAoB,UAAU,OAAO,QAAQ,WAAW;CACtE;CAEA,OAAO,QAAQ,IACb,aAAa,OAAO,WAAW,GAAG,OAAO,OAAO,QAAQ,SAAS,GAAG,SAAS,cAAc,UAAU,IAAI,MAAM,OAAO,YAAY,YAAY,MAAM,SAAS,cAAc,GAAG,KAChL;CAEA,MAAM,eAAe,mBAAmB,MAAM;CAG9C,MAAM,aAA+C,aAInD,OAAO,IAAI,aAAa;EACtB,IAAI,MAAM,eAEJ;OAAA,OAAO,qBAAqB,OADT,SAAS,WACO,GACrC,OAAO,OAAO,KAAuB;EAAA;EAGzC,OAAO,OAAO,KACZ,OAAO,SACJ,IAAI,EAAE,MAAM,MAAM,KAAK,CAAC,CAAC,CACzB,KAAK,OAAO,QAAQ,gCAAgC,0BAA0B,CAAC,CAAC,CACrF;CACF,CAAC;CAEH,IAAI;CACJ,IAAI,aAAa,aAAa;EAC5B,MAAM,aAAa,yBAAyB,OAAO,MAAM;EAIzD,SAAS,OAAO,UAHC,MAAM,SACnB,SAAS,WAAW;GAAE,GAAG;GAAQ,OAAO;EAAW,CAAC,IACpD,SAAS,KAAK;GAAE,GAAG;GAAQ,OAAO;EAAW,CAAC,CAChB,CAAC,CAAC,KAClC,OAAO,QAAQ,MAAM,MAAM,cAAc,oBAAoB,CAAC,CAChE;CACF,OAAO;EACL,MAAM,aAAa,sBAAsB,OAAO,QAAQ,WAAW;EAInE,SAAS,OAAO,UAHC,MAAM,SACnB,SAAS,WAAW;GAAE,GAAG;GAAQ,OAAO;EAAW,CAAC,IACpD,SAAS,KAAK;GAAE,GAAG;GAAQ,OAAO;EAAW,CAAC,CAChB,CAAC,CAAC,KAClC,OAAO,QAAQ,MAAM,MAAM,cAAc,iBAAiB,CAAC,CAC7D;CACF;CAEA,IAAI,OAAO,OAAO,MAAM,GAAG;EACzB,OAAO,QAAQ,IAAI,4DAA4D;EAC/E;CACF;CACA,MAAM,UAAU,OAAO;CAEvB,OAAO,QAAQ,IACb,sBAAsB,QAAQ,MAAM,oBAAoB,QAAQ,OAAO,QAAQ,IAC1E,QAAQ,KAAK,SAAS,OAAO,sBAAsB,QAAQ,KAAK,QAAQ,OAAO,qBACtF;CACA,IAAI,QAAQ,cAAc,KAAA,GACxB,OAAO,QAAQ,IAAI,UAAU,QAAQ,UAAU,MAAM,WAAW,QAAQ,UAAU,KAAK;MAClF;EACL,MAAM,cAAc,OAAO,OAAO,aAAa,qBAAqB,CAAC,CAAC,QAAQ,IAAI;EAClF,OAAO,QAAQ,IAAI,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;CACzD;AACF,CAAC,CACL,CAAC,CAAC,KACAA,QAAW,gBACT,iIACF,CACF;AAEA,MAAM,UAAUA,QAAW,IAAI,SAAS,EAAE,SAAS,QAAQ,CAAC,CAAC,CAAC,KAC5D,OAAO,UAAU,UACf,QAAQ,MACN,OAAO,GAAG,cAAc,CAAC,CAAC,KAAK,IAC3B,oBAAoB,MAAM,MAAM,YAAY,MAAM,cAAc,UAAU,MAAM,WAAW,KAC3F,OAAO,KAAK,CAClB,CACF,GACA,OAAO,QACP,OAAO,QAAQ,MAAM,MAAM,aAAa,OAAO,gBAAgB,KAAK,CAAC,CACvE;AAEA,YAAY,QAAQ,SAAS,EAAE,uBAAuB,KAAK,CAAC"}