@effect-agent/pr-review 0.1.0-beta.27 → 0.1.0-beta.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +9 -204
  2. package/dist/index.d.mts +87 -914
  3. package/dist/index.mjs +163 -71
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +3 -18
  6. package/src/index.ts +1 -25
  7. package/src/review.ts +212 -0
  8. package/dist/action.d.mts +0 -215
  9. package/dist/action.mjs +0 -505
  10. package/dist/action.mjs.map +0 -1
  11. package/dist/cli.d.mts +0 -1
  12. package/dist/cli.mjs +0 -106
  13. package/dist/cli.mjs.map +0 -1
  14. package/dist/fan-out-C3yG1cx3.d.mts +0 -1526
  15. package/dist/github-CCuLgyqb.mjs +0 -3437
  16. package/dist/github-CCuLgyqb.mjs.map +0 -1
  17. package/dist/logging-Q4j0oub-.mjs +0 -75
  18. package/dist/logging-Q4j0oub-.mjs.map +0 -1
  19. package/dist/providers-Br9FRn7j.mjs +0 -1349
  20. package/dist/providers-Br9FRn7j.mjs.map +0 -1
  21. package/dist/testing.d.mts +0 -86
  22. package/dist/testing.mjs +0 -184
  23. package/dist/testing.mjs.map +0 -1
  24. package/src/action.ts +0 -906
  25. package/src/cli.ts +0 -235
  26. package/src/internal/action-entry.ts +0 -45
  27. package/src/internal/adjudication.ts +0 -415
  28. package/src/internal/anchors.ts +0 -20
  29. package/src/internal/coverage.ts +0 -357
  30. package/src/internal/diff.ts +0 -193
  31. package/src/internal/effort.ts +0 -86
  32. package/src/internal/factory.ts +0 -357
  33. package/src/internal/fan-out-scripted.ts +0 -77
  34. package/src/internal/fan-out.ts +0 -1148
  35. package/src/internal/fingerprint.ts +0 -89
  36. package/src/internal/fixtures.ts +0 -148
  37. package/src/internal/github-env.ts +0 -164
  38. package/src/internal/github.ts +0 -1218
  39. package/src/internal/ignore.ts +0 -88
  40. package/src/internal/logging.ts +0 -124
  41. package/src/internal/profiles.ts +0 -91
  42. package/src/internal/progress.ts +0 -433
  43. package/src/internal/providers.ts +0 -133
  44. package/src/internal/render.ts +0 -819
  45. package/src/internal/retirement.ts +0 -337
  46. package/src/internal/review-agent.ts +0 -543
  47. package/src/internal/review-state.ts +0 -782
  48. package/src/internal/review-units.ts +0 -493
  49. package/src/internal/run.ts +0 -611
  50. package/src/internal/scripted.ts +0 -108
  51. package/src/internal/source.ts +0 -110
  52. package/src/testing.ts +0 -8
@@ -1,108 +0,0 @@
1
- import { Effect, Layer, Ref, Schema, Stream } from "effect";
2
- import { LanguageModel, Model, type Response } from "effect/unstable/ai";
3
-
4
- import { CodeReview } from "./review-agent.ts";
5
-
6
- // ---------------------------------------------------------------------------
7
- // Deterministic offline model for the flat reviewer: a prompt-aware scripted
8
- // model that walks the real tool surface — list, diff, read — then returns
9
- // the scripted review as its terminal JSON. Decisions key on committed
10
- // history in the prompt, never on call order, so replays stay honest.
11
- // ---------------------------------------------------------------------------
12
-
13
- export const OFFLINE_LIST_CALL_ID = "list-1";
14
- export const OFFLINE_DIFF_CALL_ID = "diff-1";
15
- export const OFFLINE_READ_CALL_ID = "read-1";
16
-
17
- /** Usage attached to EVERY scripted model turn; tests pin exact aggregates. */
18
- export const SCRIPTED_TURN_USAGE = { inputTokens: 64, outputTokens: 48 } as const;
19
-
20
- const scriptedUsage = {
21
- inputTokens: { total: SCRIPTED_TURN_USAGE.inputTokens },
22
- outputTokens: { total: SCRIPTED_TURN_USAGE.outputTokens },
23
- };
24
-
25
- export const scriptedToolTurn = (
26
- ...calls: ReadonlyArray<Response.StreamPartEncoded>
27
- ): ReadonlyArray<Response.StreamPartEncoded> => [
28
- ...calls,
29
- { type: "finish", reason: "tool-calls", usage: scriptedUsage },
30
- ];
31
-
32
- export const scriptedFinalParts = (text: string): ReadonlyArray<Response.StreamPartEncoded> => [
33
- { type: "text-start", id: "code-review" },
34
- { type: "text-delta", id: "code-review", delta: text },
35
- { type: "text-end", id: "code-review" },
36
- { type: "finish", reason: "stop", usage: scriptedUsage },
37
- ];
38
-
39
- /** A prompt-keyed scripted LanguageModel with call and prompt observability. */
40
- export const makePromptKeyedModel = (
41
- name: string,
42
- decide: (promptJson: string) => ReadonlyArray<Response.StreamPartEncoded>,
43
- ) =>
44
- Effect.gen(function* () {
45
- const calls = yield* Ref.make(0);
46
- const prompts = yield* Ref.make<ReadonlyArray<string>>([]);
47
- const model = Model.make(
48
- "scripted",
49
- name,
50
- Layer.effect(
51
- LanguageModel.LanguageModel,
52
- LanguageModel.make({
53
- generateText: () => Effect.succeed([]),
54
- streamText: (request) =>
55
- Stream.unwrap(
56
- Effect.gen(function* () {
57
- yield* Ref.update(calls, (value) => value + 1);
58
- const promptJson = JSON.stringify(request.prompt);
59
- yield* Ref.update(prompts, (previous) => [...previous, promptJson]);
60
- return Stream.fromIterable(decide(promptJson));
61
- }),
62
- ),
63
- }),
64
- ),
65
- );
66
- return { model, calls: Ref.get(calls), prompts: Ref.get(prompts) };
67
- });
68
-
69
- /**
70
- * Build the offline scripted reviewer model. Turn 1 lists the changeset,
71
- * Turn 2 reads one file diff, Turn 3 reads head context, Turn 4 returns the
72
- * scripted review JSON.
73
- */
74
- export const makeOfflineReviewerModel = (script: {
75
- readonly diffPath: string;
76
- readonly readPath: string;
77
- readonly review: CodeReview;
78
- }) =>
79
- makePromptKeyedModel("pr-review-offline", (promptJson) => {
80
- if (promptJson.includes(OFFLINE_READ_CALL_ID)) {
81
- return scriptedFinalParts(JSON.stringify(Schema.encodeSync(CodeReview)(script.review)));
82
- }
83
- if (promptJson.includes(OFFLINE_DIFF_CALL_ID)) {
84
- return scriptedToolTurn({
85
- type: "tool-call",
86
- id: OFFLINE_READ_CALL_ID,
87
- name: "read_file",
88
- params: { path: script.readPath },
89
- providerExecuted: false,
90
- });
91
- }
92
- if (promptJson.includes(OFFLINE_LIST_CALL_ID)) {
93
- return scriptedToolTurn({
94
- type: "tool-call",
95
- id: OFFLINE_DIFF_CALL_ID,
96
- name: "read_file_diff",
97
- params: { path: script.diffPath },
98
- providerExecuted: false,
99
- });
100
- }
101
- return scriptedToolTurn({
102
- type: "tool-call",
103
- id: OFFLINE_LIST_CALL_ID,
104
- name: "list_changed_files",
105
- params: { scope: "all" },
106
- providerExecuted: false,
107
- });
108
- });
@@ -1,110 +0,0 @@
1
- import { Context, Effect, Schema } from "effect";
2
-
3
- import type { ChangedFile } from "./diff.ts";
4
-
5
- // ---------------------------------------------------------------------------
6
- // The pull-request source port: everything the review tools may observe about
7
- // one pull request. The live adapter speaks the GitHub REST API; the fixture
8
- // adapter (testing entry) serves an in-memory pull request so ordinary gates
9
- // and dry runs need no network or credential.
10
- // ---------------------------------------------------------------------------
11
-
12
- /** Reading a file head version larger than this is refused, never truncated silently. */
13
- export const MAX_FILE_CHARS = 200_000;
14
-
15
- /** The changeset surface is bounded; larger pull requests fail typed. */
16
- export const MAX_CHANGED_FILES = 300;
17
-
18
- /** Pull-request identity and framing shown to the agent as its mission. */
19
- export class PullRequestMetadata extends Schema.Class<PullRequestMetadata>(
20
- "@effect-agent/pr-review/PullRequestMetadata",
21
- )({
22
- /** `owner/name`, exactly as GitHub renders it. */
23
- repository: Schema.NonEmptyString.check(Schema.isMaxLength(200)),
24
- number: Schema.Int.check(Schema.isGreaterThan(0)),
25
- title: Schema.String.check(Schema.isMaxLength(400)),
26
- /** Author-provided description; empty when the author left none. */
27
- body: Schema.String.check(Schema.isMaxLength(20_000)),
28
- baseRef: Schema.NonEmptyString.check(Schema.isMaxLength(300)),
29
- /** Exact base commit used to validate persisted incremental-review lineage. */
30
- baseSha: Schema.optionalKey(Schema.NonEmptyString.check(Schema.isMaxLength(64))),
31
- headRef: Schema.NonEmptyString.check(Schema.isMaxLength(300)),
32
- headSha: Schema.NonEmptyString.check(Schema.isMaxLength(64)),
33
- /** GitHub's own changed-file total; may exceed what `changedFiles` returns. */
34
- totalChangedFiles: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
35
- }) {}
36
-
37
- /** The upstream source failed: API error, network fault, or malformed payload. */
38
- export class PullRequestSourceFailure extends Schema.TaggedError<PullRequestSourceFailure>()(
39
- "PullRequestSourceFailure",
40
- {
41
- operation: Schema.String,
42
- reason: Schema.String,
43
- },
44
- ) {
45
- override get message() {
46
- return `Pull-request source operation '${this.operation}' failed: ${this.reason}`;
47
- }
48
- }
49
-
50
- /** A model-supplied path or range was invalid; always fail-closed (SEC-007). */
51
- export class ReviewInputViolation extends Schema.TaggedError<ReviewInputViolation>()(
52
- "ReviewInputViolation",
53
- {
54
- input: Schema.String,
55
- reason: Schema.String,
56
- },
57
- ) {
58
- override get message() {
59
- return `Rejected review input '${this.input}': ${this.reason}`;
60
- }
61
- }
62
-
63
- const BACKSLASH = String.fromCharCode(92);
64
-
65
- /**
66
- * Normalize and validate one model-supplied repository-relative path.
67
- * Absolute paths, drive letters, backslashes, empty segments, `.` and `..`
68
- * segments are all violations — never silently fixed. The changeset list is
69
- * the real allowlist; this check is defense in depth for URL construction.
70
- */
71
- export const normalizeRepoRelativePath = (
72
- path: string,
73
- ): Effect.Effect<string, ReviewInputViolation> => {
74
- const fail = (reason: string) => Effect.fail(ReviewInputViolation.make({ input: path, reason }));
75
- if (path.length === 0 || path.length > 512) {
76
- return fail("Path length is out of bounds.");
77
- }
78
- if (path.includes(BACKSLASH)) {
79
- return fail("Path contains a forbidden backslash.");
80
- }
81
- if (path.startsWith("/") || /^[A-Za-z]:/.test(path)) {
82
- return fail("Path must be repository-relative, not absolute.");
83
- }
84
- const segments = path.split("/");
85
- for (const segment of segments) {
86
- if (segment === "" || segment === "." || segment === "..") {
87
- return fail("Path segments must not be empty, '.', or '..'.");
88
- }
89
- }
90
- return Effect.succeed(segments.join("/"));
91
- };
92
-
93
- /** Read-only view of one pull request; the only repository access tools get. */
94
- export class PullRequestSource extends Context.Service<
95
- PullRequestSource,
96
- {
97
- readonly metadata: Effect.Effect<PullRequestMetadata, PullRequestSourceFailure>;
98
- /** Files exposed to the model for this run (full PR or selected delta). */
99
- readonly changedFiles: Effect.Effect<ReadonlyArray<ChangedFile>, PullRequestSourceFailure>;
100
- /** Full current PR diff used only for host-side anchor/state validation. */
101
- readonly anchorFiles: Effect.Effect<ReadonlyArray<ChangedFile>, PullRequestSourceFailure>;
102
- /**
103
- * The head-version content of one CHANGED file. Paths outside the
104
- * changeset are violations: the reviewer reads the change, not the tree.
105
- */
106
- readonly readFile: (
107
- path: string,
108
- ) => Effect.Effect<string, PullRequestSourceFailure | ReviewInputViolation>;
109
- }
110
- >()("@effect-agent/pr-review/PullRequestSource") {}
package/src/testing.ts DELETED
@@ -1,8 +0,0 @@
1
- // Deterministic test helpers: in-memory adapters for both ports and
2
- // prompt-keyed scripted models that walk the real tool surfaces. Everything
3
- // here runs with no network and no credentials, so consumers can test their
4
- // adaptations — guidance, ignore globs, extra tools, custom ports — on every
5
- // ordinary gate.
6
- export * from "./internal/fan-out-scripted.ts";
7
- export * from "./internal/fixtures.ts";
8
- export * from "./internal/scripted.ts";