dsh-auto-review 0.4.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/LICENSE +201 -0
  3. package/README.es.md +226 -0
  4. package/README.hi.md +226 -0
  5. package/README.md +226 -0
  6. package/README.pt.md +226 -0
  7. package/README.zh.md +226 -0
  8. package/cordis.patch.yml +27 -0
  9. package/lib/client.js +494 -0
  10. package/lib/client.js.map +1 -0
  11. package/lib/events-CFl6CHZi.js +344 -0
  12. package/lib/index.js +5305 -0
  13. package/lib/invariant.js +223 -0
  14. package/lib/types/client/ReviewPanel.d.ts +29 -0
  15. package/lib/types/client/ReviewPanel.d.ts.map +1 -0
  16. package/lib/types/client/ReviewPanel.js +63 -0
  17. package/lib/types/client/ReviewPanel.js.map +1 -0
  18. package/lib/types/client/index.d.ts +31 -0
  19. package/lib/types/client/index.d.ts.map +1 -0
  20. package/lib/types/client/index.js +51 -0
  21. package/lib/types/client/index.js.map +1 -0
  22. package/lib/types/client/locales.d.ts +49 -0
  23. package/lib/types/client/locales.d.ts.map +1 -0
  24. package/lib/types/client/locales.js +82 -0
  25. package/lib/types/client/locales.js.map +1 -0
  26. package/lib/types/client/present.d.ts +25 -0
  27. package/lib/types/client/present.d.ts.map +1 -0
  28. package/lib/types/client/present.js +36 -0
  29. package/lib/types/client/present.js.map +1 -0
  30. package/lib/types/client/styles.d.ts +9 -0
  31. package/lib/types/client/styles.d.ts.map +1 -0
  32. package/lib/types/client/styles.js +130 -0
  33. package/lib/types/client/styles.js.map +1 -0
  34. package/lib/types/config.d.ts +200 -0
  35. package/lib/types/config.d.ts.map +1 -0
  36. package/lib/types/config.js +154 -0
  37. package/lib/types/config.js.map +1 -0
  38. package/lib/types/events.d.ts +347 -0
  39. package/lib/types/events.d.ts.map +1 -0
  40. package/lib/types/events.js +369 -0
  41. package/lib/types/events.js.map +1 -0
  42. package/lib/types/index.d.ts +26 -0
  43. package/lib/types/index.d.ts.map +1 -0
  44. package/lib/types/index.js +26 -0
  45. package/lib/types/index.js.map +1 -0
  46. package/lib/types/invariant.d.ts +28 -0
  47. package/lib/types/invariant.d.ts.map +1 -0
  48. package/lib/types/invariant.js +306 -0
  49. package/lib/types/invariant.js.map +1 -0
  50. package/lib/types/messages.d.ts +38 -0
  51. package/lib/types/messages.d.ts.map +1 -0
  52. package/lib/types/messages.js +65 -0
  53. package/lib/types/messages.js.map +1 -0
  54. package/lib/types/projection-types.d.ts +61 -0
  55. package/lib/types/projection-types.d.ts.map +1 -0
  56. package/lib/types/projection-types.js +8 -0
  57. package/lib/types/projection-types.js.map +1 -0
  58. package/lib/types/projection.d.ts +93 -0
  59. package/lib/types/projection.d.ts.map +1 -0
  60. package/lib/types/projection.js +197 -0
  61. package/lib/types/projection.js.map +1 -0
  62. package/lib/types/review.d.ts +141 -0
  63. package/lib/types/review.d.ts.map +1 -0
  64. package/lib/types/review.js +389 -0
  65. package/lib/types/review.js.map +1 -0
  66. package/lib/types/runtime.d.ts +160 -0
  67. package/lib/types/runtime.d.ts.map +1 -0
  68. package/lib/types/runtime.js +486 -0
  69. package/lib/types/runtime.js.map +1 -0
  70. package/package.json +132 -0
  71. package/src/client/ReviewPanel.tsx +164 -0
  72. package/src/client/index.ts +72 -0
  73. package/src/client/locales.ts +87 -0
  74. package/src/client/present.ts +58 -0
  75. package/src/client/styles.ts +131 -0
  76. package/src/config.ts +346 -0
  77. package/src/events.ts +553 -0
  78. package/src/index.ts +28 -0
  79. package/src/invariant.ts +326 -0
  80. package/src/messages.ts +91 -0
  81. package/src/projection-types.ts +61 -0
  82. package/src/projection.ts +224 -0
  83. package/src/review.ts +428 -0
  84. package/src/runtime.ts +562 -0
@@ -0,0 +1,344 @@
1
+ //#region src/events.ts
2
+ /** Closed {@link AutoReviewFallback} list for runtime normalization. */
3
+ const AUTO_REVIEW_FALLBACKS = [
4
+ "timeout",
5
+ "cancelled",
6
+ "unavailable",
7
+ "schema"
8
+ ];
9
+ /**
10
+ * Brand a string as an {@link AutoReviewVerdictId}.
11
+ * @param id - the raw id string.
12
+ * @returns the same string, branded (a compile-time cast — no runtime cost).
13
+ */
14
+ function AutoReviewVerdictId(id) {
15
+ return id;
16
+ }
17
+ /**
18
+ * Brand a string as an {@link AutoReviewCircuitId}.
19
+ * @param id - the raw id string.
20
+ * @returns the same string, branded (a compile-time cast — no runtime cost).
21
+ */
22
+ function AutoReviewCircuitId(id) {
23
+ return id;
24
+ }
25
+ /**
26
+ * Brand a string as an {@link AutoReviewRejectionId}.
27
+ * @param id - the raw id string.
28
+ * @returns the same string, branded (a compile-time cast — no runtime cost).
29
+ */
30
+ function AutoReviewRejectionId(id) {
31
+ return id;
32
+ }
33
+ /** Stable marker prefix inside every injected deny reason, parsed by the invariant companion. */
34
+ const DENY_MARKER = "[auto-review]";
35
+ /** Regex extracting the reviewId from an injected deny reason. */
36
+ const DENY_MARKER_PATTERN = /\[auto-review\] review ([^\s]+) denied/u;
37
+ /** Stable marker prefix inside every injected fallback rejection text (a distinct vocabulary from {@link DENY_MARKER}). */
38
+ const FALLBACK_MARKER = "[auto-review-fallback]";
39
+ /** Regex extracting the reviewId from an injected fallback rejection. */
40
+ const FALLBACK_MARKER_PATTERN = /\[auto-review-fallback\] review ([^\s]+) failed/u;
41
+ /** Stable marker prefix inside every injected circuit rejection text. */
42
+ const CIRCUIT_MARKER = "[auto-review-circuit]";
43
+ /** Regex extracting the circuitId and tool name from an injected circuit rejection. */
44
+ const CIRCUIT_MARKER_PATTERN = /\[auto-review-circuit\] circuit ([^\s]+) rejected tool "([^"]+)" before review/u;
45
+ /** Stable marker prefix inside every injected hard-disable (`never`) rejection text. */
46
+ const NEVER_MARKER = "[auto-review-never]";
47
+ /** Regex extracting the rejectionId and tool name from an injected never rejection. */
48
+ const NEVER_MARKER_PATTERN = /\[auto-review-never\] rejection ([^\s]+) hard-disabled tool "([^"]+)"/u;
49
+ /**
50
+ * Build the model-visible deny text injected into a denied tool result. The
51
+ * embedded reviewId is what makes the model-visible text reconstructable
52
+ * from the session log (model-visible ⟺ logged).
53
+ * @param reviewId - the verdict event's id.
54
+ * @param toolName - the denied tool.
55
+ * @param reason - the reviewer's reason (already truncated).
56
+ * @returns the exact error text shown to the model.
57
+ */
58
+ function denyResultText(reviewId, toolName, reason) {
59
+ return `Error: ${DENY_MARKER} review ${reviewId} denied tool "${toolName}": ${reason}`;
60
+ }
61
+ /**
62
+ * Build the model-visible text injected into a tool result whose review
63
+ * failed and was rejected by the fallback policy. The embedded reviewId
64
+ * links it to the recorded fallback verdict (model-visible ⟺ logged).
65
+ * @param reviewId - the fallback verdict event's id.
66
+ * @param fallback - the failure category (timeout/unavailable/schema).
67
+ * @param error - the failure detail (already truncated upstream).
68
+ * @returns the exact error text shown to the model.
69
+ */
70
+ function fallbackResultText(reviewId, fallback, error) {
71
+ return `Error: ${FALLBACK_MARKER} review ${reviewId} failed (${fallback}) and the request was rejected: ${error}`;
72
+ }
73
+ /**
74
+ * Build the model-visible text injected into a tool result rejected by a
75
+ * tripped circuit breaker. The embedded circuitId links it to the recorded
76
+ * `autoReview/circuit` event (model-visible ⟺ logged).
77
+ * @param circuitId - the circuit event's id.
78
+ * @param toolName - the rejected tool.
79
+ * @param explanation - why the breaker rejects (trip kind and count).
80
+ * @returns the exact error text shown to the model.
81
+ */
82
+ function circuitResultText(circuitId, toolName, explanation) {
83
+ return `Error: ${CIRCUIT_MARKER} circuit ${circuitId} rejected tool "${toolName}" before review: ${explanation}`;
84
+ }
85
+ /**
86
+ * Build the model-visible text injected into a tool result rejected by a
87
+ * `never` policy (a hard disable: risk rule or tool-policy table entry). The
88
+ * embedded rejectionId links it to the recorded `autoReview/rejection` event
89
+ * (model-visible ⟺ logged).
90
+ * @param rejectionId - the rejection event's id.
91
+ * @param toolName - the hard-disabled tool.
92
+ * @param source - which rule or policy entry disabled it.
93
+ * @returns the exact error text shown to the model.
94
+ */
95
+ function neverResultText(rejectionId, toolName, source) {
96
+ return `Error: ${NEVER_MARKER} rejection ${rejectionId} hard-disabled tool "${toolName}": ${source}`;
97
+ }
98
+ /**
99
+ * The session's auto-review override: the last `autoReview/state` event, or
100
+ * `undefined` when the session never switched (callers apply the configured
101
+ * `enableByDefault`). The pure fold — replay IS the state.
102
+ * @param events - session events in log order (other types are skipped).
103
+ * @returns the enabled flag of the last switch, or undefined without one.
104
+ */
105
+ function effectiveAutoReviewState(events) {
106
+ for (let index = events.length - 1; index >= 0; index -= 1) {
107
+ const event = events[index];
108
+ if (event.type === "autoReview/state") return event.data.enabled;
109
+ }
110
+ }
111
+ /**
112
+ * The verdict events inside the session's CURRENT open turn, oldest first.
113
+ * A turn without `turn/start` (or one already closed by `turn/end`) yields
114
+ * an empty list.
115
+ * @param events - session events in log order.
116
+ * @returns the open turn's verdict payloads.
117
+ */
118
+ function openTurnVerdicts(events) {
119
+ let openSeq = -1;
120
+ for (let index = events.length - 1; index >= 0; index -= 1) {
121
+ const type = events[index].type;
122
+ if (type === "turn/end") return [];
123
+ if (type === "turn/start") {
124
+ openSeq = events[index].seq;
125
+ break;
126
+ }
127
+ }
128
+ if (openSeq < 0) return [];
129
+ const verdicts = [];
130
+ for (let index = events.length - 1; index >= 0; index -= 1) {
131
+ const event = events[index];
132
+ if (event.seq <= openSeq) break;
133
+ if (event.type === "autoReview/verdict") verdicts.push(event.data);
134
+ }
135
+ verdicts.reverse();
136
+ return verdicts;
137
+ }
138
+ /**
139
+ * How many REAL AI verdicts the session's CURRENT open turn has consumed.
140
+ * Fallback verdicts do not count: they are budgeted separately by
141
+ * {@link autoReviewFailuresInOpenTurn}, so a failing reviewer does not eat
142
+ * the AI-decision budget. A turn without `turn/start` (or one already closed
143
+ * by `turn/end`) counts zero.
144
+ * @param events - session events in log order.
145
+ * @returns the decision-carrying verdict count inside the current open turn.
146
+ */
147
+ function autoReviewsInOpenTurn(events) {
148
+ return openTurnVerdicts(events).filter((data) => data.decision !== void 0).length;
149
+ }
150
+ /**
151
+ * How many reviewer FAILURES the session's CURRENT open turn has consumed —
152
+ * `autoReview/verdict` events carrying a fallback other than `cancelled`
153
+ * (a user withdrawal is not a reviewer failure). The answerer delegates once
154
+ * the budget is exhausted so a broken reviewer costs one timeout instead of
155
+ * `maxFailuresPerTurn` of them.
156
+ * @param events - session events in log order.
157
+ * @returns the failure count inside the current open turn.
158
+ */
159
+ function autoReviewFailuresInOpenTurn(events) {
160
+ return openTurnVerdicts(events).filter((data) => data.fallback !== void 0 && data.fallback !== "cancelled").length;
161
+ }
162
+ /**
163
+ * Whether a verdict is a denial of the request: a reviewer deny, or an allow
164
+ * overridden by the risk policy (`escalation: 'risk-policy'` with the deny
165
+ * outcome). The circuit breaker and the approve list count both.
166
+ * @param data - the verdict payload.
167
+ * @returns true when the request was denied.
168
+ */
169
+ function isDenial(data) {
170
+ return data.decision === "deny" || data.escalation === "risk-policy";
171
+ }
172
+ /**
173
+ * The trailing run of consecutive denials in the current open turn —
174
+ * the circuit breaker's `consecutiveDenies` signal.
175
+ * @param events - session events in log order.
176
+ * @returns the length of the trailing denial run.
177
+ */
178
+ function consecutiveDeniesInOpenTurn(events) {
179
+ const verdicts = openTurnVerdicts(events);
180
+ let count = 0;
181
+ for (let index = verdicts.length - 1; index >= 0; index -= 1) {
182
+ if (!isDenial(verdicts[index])) break;
183
+ count += 1;
184
+ }
185
+ return count;
186
+ }
187
+ /**
188
+ * How many of the last {@link windowSize} decision verdicts in the current
189
+ * open turn are denials — the circuit breaker's `windowDenies` signal.
190
+ * @param events - session events in log order.
191
+ * @param windowSize - how many recent verdicts the window counts.
192
+ * @returns the denial count inside the window.
193
+ */
194
+ function deniesInRecentVerdicts(events, windowSize) {
195
+ return openTurnVerdicts(events).filter((data) => data.decision !== void 0).slice(-windowSize).filter((data) => isDenial(data)).length;
196
+ }
197
+ /**
198
+ * The current open turn's circuit trip, when one was recorded.
199
+ * @param events - session events in log order.
200
+ * @returns the last `autoReview/circuit` payload in the open turn, or undefined.
201
+ */
202
+ function circuitInOpenTurn(events) {
203
+ let openSeq = -1;
204
+ for (let index = events.length - 1; index >= 0; index -= 1) {
205
+ const type = events[index].type;
206
+ if (type === "turn/end") return void 0;
207
+ if (type === "turn/start") {
208
+ openSeq = events[index].seq;
209
+ break;
210
+ }
211
+ }
212
+ if (openSeq < 0) return void 0;
213
+ for (let index = events.length - 1; index >= 0; index -= 1) {
214
+ const event = events[index];
215
+ if (event.seq <= openSeq) break;
216
+ if (event.type === "autoReview/circuit") return event.data;
217
+ }
218
+ }
219
+ /**
220
+ * The most recent deny verdicts, newest first — the `/auto-review approve`
221
+ * candidate list.
222
+ * @param events - session events in log order.
223
+ * @param limit - how many to collect.
224
+ * @returns up to {@link limit} deny verdicts, newest first.
225
+ */
226
+ function lastDeniedVerdicts(events, limit) {
227
+ const result = [];
228
+ for (let index = events.length - 1; index >= 0 && result.length < limit; index -= 1) {
229
+ const event = events[index];
230
+ if (event.type !== "autoReview/verdict" || !isDenial(event.data)) continue;
231
+ const data = event.data;
232
+ result.push({
233
+ reviewId: data.reviewId,
234
+ toolName: data.toolName,
235
+ ...data.reason !== void 0 ? { reason: data.reason } : {},
236
+ ...data.riskLevel !== void 0 ? { riskLevel: data.riskLevel } : {}
237
+ });
238
+ }
239
+ return result;
240
+ }
241
+ /**
242
+ * Whether a human one-shot override is pending for a tool: the latest
243
+ * `autoReview/override` for the tool is still unconsumed (no verdict for the
244
+ * tool after it) and unexpired. An override is consumed by the NEXT
245
+ * same-tool review regardless of its decision — single-use by construction.
246
+ * @param events - session events in log order.
247
+ * @param toolName - the tool an approval request is about.
248
+ * @param ttlMs - the override lifetime.
249
+ * @param now - the current epoch milliseconds.
250
+ * @returns the overridden deny verdict's id, or undefined without a pending override.
251
+ */
252
+ function activeOverride(events, toolName, ttlMs, now) {
253
+ for (let index = events.length - 1; index >= 0; index -= 1) {
254
+ const event = events[index];
255
+ if (event.type === "autoReview/verdict" && event.data.toolName === toolName) return void 0;
256
+ if (event.type === "autoReview/override" && event.data.toolName === toolName) return now - event.time <= ttlMs ? event.data.reviewId : void 0;
257
+ }
258
+ }
259
+ /**
260
+ * Fold one session's review statistics: verdict counts, never-rejection
261
+ * count, mean duration, and the three most recent verdicts.
262
+ * @param events - session events in log order.
263
+ * @returns the statistics.
264
+ */
265
+ function reviewStats(events) {
266
+ let allows = 0;
267
+ let denies = 0;
268
+ let fallbacks = 0;
269
+ let rejections = 0;
270
+ let decided = 0;
271
+ let durationSum = 0;
272
+ for (const event of events) {
273
+ if (event.type === "autoReview/rejection") {
274
+ rejections += 1;
275
+ continue;
276
+ }
277
+ if (event.type !== "autoReview/verdict") continue;
278
+ const data = event.data;
279
+ if (data.decision === "allow") allows += 1;
280
+ else if (data.decision === "deny") denies += 1;
281
+ else fallbacks += 1;
282
+ if (data.decision !== void 0) {
283
+ decided += 1;
284
+ durationSum += data.durationMs;
285
+ }
286
+ }
287
+ const recent = [];
288
+ for (let index = events.length - 1; index >= 0 && recent.length < 3; index -= 1) {
289
+ const event = events[index];
290
+ if (event.type === "autoReview/verdict") recent.push(event.data);
291
+ }
292
+ return {
293
+ allows,
294
+ denies,
295
+ fallbacks,
296
+ rejections,
297
+ avgDurationMs: decided === 0 ? 0 : Math.round(durationSum / decided),
298
+ recent
299
+ };
300
+ }
301
+ /**
302
+ * Correlate a pending approval request with the `approval/asked` event the
303
+ * service just appended. The service appends the asked event synchronously
304
+ * before waterfall dispatch, so the matching event is the last unpaired
305
+ * asked event whose call id (or, without one, tool name) matches the request.
306
+ * @param events - session events in log order.
307
+ * @param toolName - the request's tool name.
308
+ * @param callId - the request's call id, when present.
309
+ * @returns the asked event's approval id, or undefined when the chain is broken.
310
+ */
311
+ function correlateApprovalId(events, toolName, callId) {
312
+ const decided = /* @__PURE__ */ new Set();
313
+ let candidate;
314
+ for (let index = events.length - 1; index >= 0; index -= 1) {
315
+ const event = events[index];
316
+ if (event.type === "approval/decided") {
317
+ decided.add(event.data.id);
318
+ continue;
319
+ }
320
+ if (event.type !== "approval/asked") continue;
321
+ const asked = event.data;
322
+ if (decided.has(asked.id)) continue;
323
+ if (callId !== void 0 ? asked.callId === callId : asked.toolName === toolName) {
324
+ candidate = asked.id;
325
+ break;
326
+ }
327
+ }
328
+ return candidate;
329
+ }
330
+ /**
331
+ * Find the already-presented tool call the approval request refers to — the
332
+ * `tool/call` event whose arguments the model produced and the UI streamed.
333
+ * @param events - session events in log order.
334
+ * @param callId - the exact call to find.
335
+ * @returns the call's raw arguments JSON string, or undefined without one.
336
+ */
337
+ function findPresentedCall(events, callId) {
338
+ for (let index = events.length - 1; index >= 0; index -= 1) {
339
+ const event = events[index];
340
+ if (event.type === "tool/call" && event.data.callId === callId) return event.data.arguments;
341
+ }
342
+ }
343
+ //#endregion
344
+ export { fallbackResultText as C, neverResultText as D, lastDeniedVerdicts as E, openTurnVerdicts as O, effectiveAutoReviewState as S, isDenial as T, circuitResultText as _, CIRCUIT_MARKER as a, deniesInRecentVerdicts as b, DENY_MARKER_PATTERN as c, NEVER_MARKER as d, NEVER_MARKER_PATTERN as f, circuitInOpenTurn as g, autoReviewsInOpenTurn as h, AutoReviewVerdictId as i, reviewStats as k, FALLBACK_MARKER as l, autoReviewFailuresInOpenTurn as m, AutoReviewCircuitId as n, CIRCUIT_MARKER_PATTERN as o, activeOverride as p, AutoReviewRejectionId as r, DENY_MARKER as s, AUTO_REVIEW_FALLBACKS as t, FALLBACK_MARKER_PATTERN as u, consecutiveDeniesInOpenTurn as v, findPresentedCall as w, denyResultText as x, correlateApprovalId as y };