@erichll/pi-auto-review 0.15.3 → 0.17.0
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/CHANGELOG.md +25 -0
- package/package.json +8 -7
- package/src/broker/overrides.ts +1 -0
- package/src/index.ts +55 -1944
- package/src/policy-audit/classifier.ts +1 -1
- package/src/policy-audit/index.ts +2 -1
- package/src/policy.ts +19 -8
- package/src/review/audit.ts +14 -0
- package/src/review/complete.ts +347 -0
- package/src/review/config.ts +366 -0
- package/src/review/consts.ts +22 -0
- package/src/review/guards.ts +66 -0
- package/src/review/index.ts +61 -0
- package/src/review/input.ts +439 -0
- package/src/review/prompts.ts +33 -0
- package/src/review/provider.ts +583 -0
- package/src/review/types.ts +207 -0
- package/src/user-feedback.ts +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
completeSimple,
|
|
3
|
+
type ApiStreamSimpleFunction,
|
|
4
|
+
} from "@earendil-works/pi-ai/compat";
|
|
5
|
+
import type { ModelDecision, TranscriptResult } from "../policy.ts";
|
|
6
|
+
import type { PolicyAuditConfig } from "../policy-audit/index.ts";
|
|
7
|
+
|
|
8
|
+
export type ReasoningLevel =
|
|
9
|
+
| "off"
|
|
10
|
+
| "minimal"
|
|
11
|
+
| "low"
|
|
12
|
+
| "medium"
|
|
13
|
+
| "high"
|
|
14
|
+
| "xhigh"
|
|
15
|
+
| "max";
|
|
16
|
+
|
|
17
|
+
export type BoundedSurface = "external_directory" | "path";
|
|
18
|
+
|
|
19
|
+
export type Config = {
|
|
20
|
+
model: string;
|
|
21
|
+
reasoning: ReasoningLevel;
|
|
22
|
+
timeoutMs: number;
|
|
23
|
+
maxTokens: number;
|
|
24
|
+
retries: number;
|
|
25
|
+
maxUserTranscriptTokens: number;
|
|
26
|
+
maxToolTranscriptTokens: number;
|
|
27
|
+
maxRelevantResultTokens: number;
|
|
28
|
+
maxReviewerInputTokens: number;
|
|
29
|
+
breakGlassEnabled: boolean;
|
|
30
|
+
failureMode: "deny" | "defer";
|
|
31
|
+
grantTtlMs: number;
|
|
32
|
+
autoConfirmBoundedAllows: readonly BoundedSurface[];
|
|
33
|
+
policyAudit: Readonly<PolicyAuditConfig>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type CompletionMessage = {
|
|
37
|
+
stopReason?: string;
|
|
38
|
+
responseModel?: string;
|
|
39
|
+
errorMessage?: string;
|
|
40
|
+
content?: Array<{ type?: string; text?: string }>;
|
|
41
|
+
usage?: {
|
|
42
|
+
input?: number;
|
|
43
|
+
output?: number;
|
|
44
|
+
cacheRead?: number;
|
|
45
|
+
cacheWrite?: number;
|
|
46
|
+
reasoning?: number;
|
|
47
|
+
totalTokens?: number;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type UsageAvailability =
|
|
52
|
+
| "reported"
|
|
53
|
+
| "estimated"
|
|
54
|
+
| "unavailable"
|
|
55
|
+
| "unknown_provenance";
|
|
56
|
+
|
|
57
|
+
export type ReviewUsage = {
|
|
58
|
+
input?: number;
|
|
59
|
+
output?: number;
|
|
60
|
+
cacheRead?: number;
|
|
61
|
+
cacheWrite?: number;
|
|
62
|
+
reasoning?: number;
|
|
63
|
+
totalTokens?: number;
|
|
64
|
+
observedInputTokens?: number;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type ReviewAttemptStatus =
|
|
68
|
+
| "success"
|
|
69
|
+
| "format_error"
|
|
70
|
+
| "non_stop"
|
|
71
|
+
| "transport_failure"
|
|
72
|
+
| "timeout"
|
|
73
|
+
| "abort";
|
|
74
|
+
|
|
75
|
+
export type ReviewErrorClass =
|
|
76
|
+
| "none"
|
|
77
|
+
| "non_json"
|
|
78
|
+
| "schema"
|
|
79
|
+
| "empty_output"
|
|
80
|
+
| "output_limit"
|
|
81
|
+
| "provider_stop"
|
|
82
|
+
| "transient_connection"
|
|
83
|
+
| "transient_server"
|
|
84
|
+
| "rate_limit"
|
|
85
|
+
| "timeout"
|
|
86
|
+
| "abort"
|
|
87
|
+
| "authentication"
|
|
88
|
+
| "model_resolution"
|
|
89
|
+
| "request_configuration"
|
|
90
|
+
| "circuit_breaker"
|
|
91
|
+
| "critical_evidence_overflow"
|
|
92
|
+
| "required_profile_overflow"
|
|
93
|
+
| "reviewer_input_budget_exceeded"
|
|
94
|
+
| "unknown";
|
|
95
|
+
|
|
96
|
+
export type ReviewAttemptObservation = {
|
|
97
|
+
attempt: number;
|
|
98
|
+
model: string;
|
|
99
|
+
status: ReviewAttemptStatus;
|
|
100
|
+
errorClass: ReviewErrorClass;
|
|
101
|
+
stopReason: "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred" | "unknown";
|
|
102
|
+
durationMs: number;
|
|
103
|
+
willRetry: boolean;
|
|
104
|
+
usageAvailability: UsageAvailability;
|
|
105
|
+
usage: ReviewUsage;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type PreflightPart = { characters: number; estimatedTokens: number };
|
|
109
|
+
|
|
110
|
+
export type ReviewPreflight = {
|
|
111
|
+
estimator: "conservative:cjk-aware";
|
|
112
|
+
maxReviewerInputTokens: number;
|
|
113
|
+
framingReserveTokens: number;
|
|
114
|
+
fixedPrompt: PreflightPart;
|
|
115
|
+
canonicalRequest: PreflightPart;
|
|
116
|
+
override: PreflightPart;
|
|
117
|
+
user: PreflightPart;
|
|
118
|
+
tool: PreflightPart;
|
|
119
|
+
relevantResult: PreflightPart;
|
|
120
|
+
framing: PreflightPart;
|
|
121
|
+
total: PreflightPart;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export type ReviewExecutionSummary = {
|
|
125
|
+
attempts: ReviewAttemptObservation[];
|
|
126
|
+
errorCounts: Partial<Record<Exclude<ReviewErrorClass, "none">, number>>;
|
|
127
|
+
durationMs: number;
|
|
128
|
+
transcript: TranscriptResult;
|
|
129
|
+
preflight: ReviewPreflight;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export type ReviewerTelemetryEvent =
|
|
133
|
+
| ({
|
|
134
|
+
type: "review_attempt";
|
|
135
|
+
requestId: string;
|
|
136
|
+
surface: string;
|
|
137
|
+
} & ReviewAttemptObservation)
|
|
138
|
+
| {
|
|
139
|
+
type: "review_complete";
|
|
140
|
+
requestId: string;
|
|
141
|
+
surface: string;
|
|
142
|
+
model: string;
|
|
143
|
+
reasoning: ReasoningLevel;
|
|
144
|
+
outcome: "allow" | "deny" | "defer";
|
|
145
|
+
failureMode?: "deny" | "defer";
|
|
146
|
+
attempts: number;
|
|
147
|
+
errorCounts: ReviewExecutionSummary["errorCounts"];
|
|
148
|
+
durationMs: number;
|
|
149
|
+
usageAvailability: UsageAvailability;
|
|
150
|
+
usage: ReviewUsage;
|
|
151
|
+
transcript: {
|
|
152
|
+
userCharacters: number;
|
|
153
|
+
toolCharacters: number;
|
|
154
|
+
relevantResultCharacters: number;
|
|
155
|
+
truncated: boolean;
|
|
156
|
+
selectedCandidates: TranscriptResult["selectedCandidates"];
|
|
157
|
+
failureCode?: TranscriptResult["failureCode"];
|
|
158
|
+
userAuthorizationCeiling: TranscriptResult["userAuthorizationCeiling"];
|
|
159
|
+
userConstraint: TranscriptResult["userConstraint"];
|
|
160
|
+
compactionState: TranscriptResult["compactionState"];
|
|
161
|
+
budgetRemovals: TranscriptResult["budgetRemovals"];
|
|
162
|
+
};
|
|
163
|
+
preflight: ReviewPreflight;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export type ReviewerRuntime = {
|
|
167
|
+
model: Parameters<typeof completeSimple>[0];
|
|
168
|
+
auth: {
|
|
169
|
+
apiKey?: string;
|
|
170
|
+
// ProviderHeaders allows null values (e.g. "clear" directives) in 0.84.x.
|
|
171
|
+
headers?: Record<string, string | null>;
|
|
172
|
+
env?: Record<string, string>;
|
|
173
|
+
};
|
|
174
|
+
streamSimple?: ApiStreamSimpleFunction;
|
|
175
|
+
sessionId: string;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Model/stream metadata is re-resolved for every review instead of being
|
|
179
|
+
// cached for the session: pi can refresh models.json or re-register a
|
|
180
|
+
// provider while a session is live, and the resolved model's baseUrl, api
|
|
181
|
+
// or streamSimple may have changed, so a per-session cache could keep
|
|
182
|
+
// calling a stale endpoint. Authentication is also deliberately excluded
|
|
183
|
+
// because pi resolves models.json auth and headers dynamically on every
|
|
184
|
+
// request (including OAuth refresh); it is reacquired per model call in
|
|
185
|
+
// complete().
|
|
186
|
+
export type ReviewerMeta = Omit<ReviewerRuntime, "auth" | "sessionId">;
|
|
187
|
+
|
|
188
|
+
export type ReviewResult = {
|
|
189
|
+
decision: ModelDecision;
|
|
190
|
+
attempts: number;
|
|
191
|
+
retryErrors: ReviewErrorClass[];
|
|
192
|
+
durationMs: number;
|
|
193
|
+
transcript: TranscriptResult;
|
|
194
|
+
summary: ReviewExecutionSummary;
|
|
195
|
+
unavailable?: boolean;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export class ReviewExecutionError extends Error {
|
|
199
|
+
constructor(
|
|
200
|
+
readonly errorClass: ReviewErrorClass,
|
|
201
|
+
readonly summary: ReviewExecutionSummary,
|
|
202
|
+
) {
|
|
203
|
+
super(`automatic review failed (${errorClass})`);
|
|
204
|
+
this.name = "ReviewExecutionError";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
package/src/user-feedback.ts
CHANGED
|
@@ -454,7 +454,7 @@ export function buildUserReviewGroupLines(
|
|
|
454
454
|
data: UserReviewGroupEntryData,
|
|
455
455
|
): UserReviewEntryData {
|
|
456
456
|
if (data.members.length === 1) {
|
|
457
|
-
const member = data.members[0]
|
|
457
|
+
const member = data.members[0]!;
|
|
458
458
|
const lines = buildUserReviewLines(member);
|
|
459
459
|
if (member.permissionResult === "deny" && member.outcome !== "deny") {
|
|
460
460
|
lines.push("Local confirmation · denied");
|