@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.
- package/README.md +9 -204
- package/dist/index.d.mts +87 -914
- package/dist/index.mjs +163 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -18
- package/src/index.ts +1 -25
- package/src/review.ts +212 -0
- package/dist/action.d.mts +0 -215
- package/dist/action.mjs +0 -505
- package/dist/action.mjs.map +0 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -106
- package/dist/cli.mjs.map +0 -1
- package/dist/fan-out-C3yG1cx3.d.mts +0 -1526
- package/dist/github-CCuLgyqb.mjs +0 -3437
- package/dist/github-CCuLgyqb.mjs.map +0 -1
- package/dist/logging-Q4j0oub-.mjs +0 -75
- package/dist/logging-Q4j0oub-.mjs.map +0 -1
- package/dist/providers-Br9FRn7j.mjs +0 -1349
- package/dist/providers-Br9FRn7j.mjs.map +0 -1
- package/dist/testing.d.mts +0 -86
- package/dist/testing.mjs +0 -184
- package/dist/testing.mjs.map +0 -1
- package/src/action.ts +0 -906
- package/src/cli.ts +0 -235
- package/src/internal/action-entry.ts +0 -45
- package/src/internal/adjudication.ts +0 -415
- package/src/internal/anchors.ts +0 -20
- package/src/internal/coverage.ts +0 -357
- package/src/internal/diff.ts +0 -193
- package/src/internal/effort.ts +0 -86
- package/src/internal/factory.ts +0 -357
- package/src/internal/fan-out-scripted.ts +0 -77
- package/src/internal/fan-out.ts +0 -1148
- package/src/internal/fingerprint.ts +0 -89
- package/src/internal/fixtures.ts +0 -148
- package/src/internal/github-env.ts +0 -164
- package/src/internal/github.ts +0 -1218
- package/src/internal/ignore.ts +0 -88
- package/src/internal/logging.ts +0 -124
- package/src/internal/profiles.ts +0 -91
- package/src/internal/progress.ts +0 -433
- package/src/internal/providers.ts +0 -133
- package/src/internal/render.ts +0 -819
- package/src/internal/retirement.ts +0 -337
- package/src/internal/review-agent.ts +0 -543
- package/src/internal/review-state.ts +0 -782
- package/src/internal/review-units.ts +0 -493
- package/src/internal/run.ts +0 -611
- package/src/internal/scripted.ts +0 -108
- package/src/internal/source.ts +0 -110
- package/src/testing.ts +0 -8
package/src/internal/scripted.ts
DELETED
|
@@ -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
|
-
});
|
package/src/internal/source.ts
DELETED
|
@@ -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";
|