opencode-reviewer 0.6.1 → 0.7.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/README.md +13 -5
- package/dist/tui.js +126 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,11 +116,19 @@ costs use reported charges; other endpoints use available catalog estimates.
|
|
|
116
116
|
Received usage counts even if a review fails or is interrupted. Unreported charges
|
|
117
117
|
are missing from the totals.
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
review,
|
|
123
|
-
|
|
119
|
+
The lifetime dialog shows completed reviews, retries, tokens, cost, Safe/Unsafe
|
|
120
|
+
percentages, confirmed auto-approvals, and average time to a rating and full report.
|
|
121
|
+
Percentages use recorded completed reviews as their denominator. Each validated
|
|
122
|
+
review counts once, even without usage data; a fresh review after re-enabling counts
|
|
123
|
+
again. Retries count extra API attempts actually dispatched, including format
|
|
124
|
+
corrections and transport recovery. Manual approvals are not auto-approvals.
|
|
125
|
+
Timings run from evaluation start, including evidence and retries, to the accepted
|
|
126
|
+
attempt's first rating and final validated response, excluding rendering/countdown
|
|
127
|
+
time. Non-streaming reviews use the final response time for both measurements.
|
|
128
|
+
Only running averages and sample counts are stored, not individual timings.
|
|
129
|
+
Totals persist across restarts. New metrics show partial history when older records
|
|
130
|
+
lack them; averages include measured reviews only. Earlier unrecorded values cannot
|
|
131
|
+
be reconstructed.
|
|
124
132
|
|
|
125
133
|
Reviews send the pending request, latest user prompt, project context, and relevant
|
|
126
134
|
file snapshots, diffs, or tool arguments to your endpoint before approval. Shell
|
package/dist/tui.js
CHANGED
|
@@ -1182,6 +1182,16 @@ var uiText = {
|
|
|
1182
1182
|
pricingCoverage: (available, total) => `Pricing available: ${available}/${total} requests`,
|
|
1183
1183
|
since: (date) => `Recorded since: ${date}`,
|
|
1184
1184
|
ratings: (safe, unsafe) => `${safe} \u2713 ${unsafe} \u2717`,
|
|
1185
|
+
dialogReviews: (count3) => `Reviews: ${count3}`,
|
|
1186
|
+
dialogRetries: (count3, partial) => `Retries: ${count3}${partial ? " (partial history)" : ""}`,
|
|
1187
|
+
dialogTokens: (input, output, partial) => `Tokens: ${input} in ${output} out${partial ? " (partial coverage)" : ""}`,
|
|
1188
|
+
dialogTokensUnavailable: "Tokens: unavailable",
|
|
1189
|
+
dialogCost: (amount2, partial) => amount2 === void 0 ? "Cost: unavailable" : `Cost: $${amount2}${partial ? " (partial pricing)" : ""}`,
|
|
1190
|
+
dialogSafe: (count3, percentage) => `Safe: ${count3} (${percentage === void 0 ? "n/a" : `${percentage}%`})`,
|
|
1191
|
+
dialogUnsafe: (count3, percentage) => `Unsafe: ${count3} (${percentage === void 0 ? "n/a" : `${percentage}%`})`,
|
|
1192
|
+
dialogAutoApproved: (count3, percentage, partial) => `Auto-approved: ${count3} (${percentage === void 0 ? "n/a" : `${percentage}%`})${partial ? " (partial history)" : ""}`,
|
|
1193
|
+
averageFullReport: (seconds) => `Average time to full report: ${seconds === void 0 ? "unavailable" : `${seconds}s`}`,
|
|
1194
|
+
averageRating: (seconds) => `Average time to rating: ${seconds === void 0 ? "unavailable" : `${seconds}s`}`,
|
|
1185
1195
|
ratingsSince: (date) => `Ratings recorded since: ${date}`,
|
|
1186
1196
|
ratingsExplanation: "Ratings count completed, validated reviews, including those without usage data. Previews, retries and failed or interrupted analyses do not count. A fresh review after re-enabling counts again. Earlier ratings were not recorded and cannot be recovered.",
|
|
1187
1197
|
explanation: "Received usage only, including interrupted or unsuccessful reviews. Unreported charges remain unknown. Costs combine endpoint-reported amounts and catalog estimates; legacy history retains its original estimates. Earlier unrecorded usage cannot be recovered."
|
|
@@ -1817,11 +1827,11 @@ async function streamedAssessment(response, signal, accounting, progress, firstC
|
|
|
1817
1827
|
if (!done || !stopped) throw new Error("Reviewer stream ended without stop and DONE");
|
|
1818
1828
|
return assessment;
|
|
1819
1829
|
}
|
|
1820
|
-
async function review(evidence, config, signal, fetcher = fetch, environment = process.env, prompts = BUILTIN_PROMPTS, pricing, onUsage, onProgress, onDiagnostics) {
|
|
1830
|
+
async function review(evidence, config, signal, fetcher = fetch, environment = process.env, prompts = BUILTIN_PROMPTS, pricing, onUsage, onProgress, onDiagnostics, onRetry) {
|
|
1821
1831
|
if (remainingTime(signal) === Infinity) {
|
|
1822
1832
|
let worker;
|
|
1823
1833
|
try {
|
|
1824
|
-
return await withDeadline(signal, config.timeoutMs, (bounded) => worker = review(evidence, config, bounded, fetcher, environment, prompts, pricing, onUsage, onProgress, onDiagnostics));
|
|
1834
|
+
return await withDeadline(signal, config.timeoutMs, (bounded) => worker = review(evidence, config, bounded, fetcher, environment, prompts, pricing, onUsage, onProgress, onDiagnostics, onRetry));
|
|
1825
1835
|
} finally {
|
|
1826
1836
|
await worker?.catch(() => {
|
|
1827
1837
|
});
|
|
@@ -1867,13 +1877,26 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1867
1877
|
});
|
|
1868
1878
|
reviewStage(signal, "Reviewer response");
|
|
1869
1879
|
diagnose = onDiagnostics ? diagnosticAttempt(onDiagnostics, attempt) : void 0;
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1880
|
+
signal.throwIfAborted();
|
|
1881
|
+
let pending;
|
|
1882
|
+
try {
|
|
1883
|
+
pending = fetcher(`${config.baseURL}/chat/completions`, {
|
|
1884
|
+
method: "POST",
|
|
1885
|
+
redirect: "error",
|
|
1886
|
+
signal: AbortSignal.any([signal, requestAbort.signal]),
|
|
1887
|
+
headers: { "Content-Type": "application/json", ...key ? { Authorization: `Bearer ${key}` } : {} },
|
|
1888
|
+
body
|
|
1889
|
+
});
|
|
1890
|
+
} finally {
|
|
1891
|
+
if (attempt > 0) {
|
|
1892
|
+
try {
|
|
1893
|
+
void Promise.resolve(onRetry?.()).catch(() => {
|
|
1894
|
+
});
|
|
1895
|
+
} catch {
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
response = await abortable(pending, signal, cancelBody);
|
|
1877
1900
|
diagnose?.("headers");
|
|
1878
1901
|
} catch (error) {
|
|
1879
1902
|
signal.throwIfAborted();
|
|
@@ -2185,6 +2208,7 @@ var Controller = class {
|
|
|
2185
2208
|
if (value.attempt !== entry.attempt + 1 || value.phase !== (value.attempt ? "retrying" : "evaluating")) return;
|
|
2186
2209
|
this.clearProgress(entry);
|
|
2187
2210
|
entry.attempt = value.attempt;
|
|
2211
|
+
entry.ratingAt = void 0;
|
|
2188
2212
|
entry.view = { ...entry.view, progress: { attempt: value.attempt, phase: value.phase } };
|
|
2189
2213
|
this.publish();
|
|
2190
2214
|
return;
|
|
@@ -2200,6 +2224,7 @@ var Controller = class {
|
|
|
2200
2224
|
...typeof value.preview.safe === "boolean" ? { safe: value.preview.safe } : {},
|
|
2201
2225
|
...typeof value.preview.desc === "string" ? { desc: value.preview.desc } : {}
|
|
2202
2226
|
};
|
|
2227
|
+
if (typeof preview.safe === "boolean" && entry.ratingAt === void 0) entry.ratingAt = this.time.now();
|
|
2203
2228
|
entry.pendingProgress = { attempt: value.attempt, phase: "streaming", preview };
|
|
2204
2229
|
if (preview.safe !== entry.view.progress?.preview?.safe) {
|
|
2205
2230
|
const desc = entry.view.progress?.preview?.desc;
|
|
@@ -2379,6 +2404,7 @@ var Controller = class {
|
|
|
2379
2404
|
return null;
|
|
2380
2405
|
}
|
|
2381
2406
|
}
|
|
2407
|
+
entry.startedAt = this.time.now();
|
|
2382
2408
|
return this.evaluate(request, entry.abort.signal, () => {
|
|
2383
2409
|
if (active() && (entry.view.status === "identifying" || entry.view.status === "analyzing")) {
|
|
2384
2410
|
entry.view = { ...entry.view, status: "analyzing" };
|
|
@@ -2391,7 +2417,9 @@ var Controller = class {
|
|
|
2391
2417
|
entry.view = assessment ? { ...entry.view, status: "complete", assessment, progress: void 0 } : { ...entry.view, status: "unrelated", progress: void 0 };
|
|
2392
2418
|
if (assessment) {
|
|
2393
2419
|
try {
|
|
2394
|
-
|
|
2420
|
+
const fullReportMs = Math.max(0, this.time.now() - entry.startedAt);
|
|
2421
|
+
const ratingMs = entry.ratingAt === void 0 ? fullReportMs : Math.max(0, entry.ratingAt - entry.startedAt);
|
|
2422
|
+
void Promise.resolve(this.ratingObserver?.(assessment.safe, { fullReportMs, ratingMs })).catch(() => {
|
|
2395
2423
|
});
|
|
2396
2424
|
} catch {
|
|
2397
2425
|
}
|
|
@@ -3646,6 +3674,16 @@ import { constants as constants3 } from "node:fs";
|
|
|
3646
3674
|
import { mkdir, open as open3, readdir as readdir2, rename, unlink } from "node:fs/promises";
|
|
3647
3675
|
import { randomUUID } from "node:crypto";
|
|
3648
3676
|
import path8 from "node:path";
|
|
3677
|
+
var emptyActivity = () => ({
|
|
3678
|
+
reviews: 0,
|
|
3679
|
+
usageRequests: 0,
|
|
3680
|
+
retries: 0,
|
|
3681
|
+
autoApproved: 0,
|
|
3682
|
+
timedReviews: 0,
|
|
3683
|
+
meanFullReportMs: 0,
|
|
3684
|
+
meanRatingMs: 0,
|
|
3685
|
+
since: null
|
|
3686
|
+
});
|
|
3649
3687
|
var empty = () => ({
|
|
3650
3688
|
requests: 0,
|
|
3651
3689
|
tokenRequests: 0,
|
|
@@ -3656,7 +3694,8 @@ var empty = () => ({
|
|
|
3656
3694
|
since: null,
|
|
3657
3695
|
safe: 0,
|
|
3658
3696
|
unsafe: 0,
|
|
3659
|
-
ratingsSince: null
|
|
3697
|
+
ratingsSince: null,
|
|
3698
|
+
activity: emptyActivity()
|
|
3660
3699
|
});
|
|
3661
3700
|
var count2 = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3662
3701
|
var amount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
@@ -3665,8 +3704,15 @@ function validate(value) {
|
|
|
3665
3704
|
if (!value || ![value.requests, value.tokenRequests, value.input, value.output, value.priced, value.safe, value.unsafe, value.safe + value.unsafe].every(count2) || !amount(value.cost) || value.priced > value.requests || value.tokenRequests > value.requests || value.requests > value.tokenRequests + value.priced || !value.tokenRequests && (value.input !== 0 || value.output !== 0) || (value.requests > 0 ? !count2(value.since) || value.since > 864e13 : value.since !== null || value.input !== 0 || value.output !== 0) || !value.priced && value.cost !== 0 || (value.safe + value.unsafe > 0 ? !count2(value.ratingsSince) || value.ratingsSince > 864e13 : value.ratingsSince !== null)) {
|
|
3666
3705
|
throw new Error("Invalid lifetime usage totals");
|
|
3667
3706
|
}
|
|
3707
|
+
const activity = value.activity;
|
|
3708
|
+
if (!activity || ![activity.reviews, activity.usageRequests, activity.retries, activity.autoApproved, activity.timedReviews].every(count2) || activity.reviews > value.safe + value.unsafe || activity.usageRequests > value.requests || activity.timedReviews > activity.reviews || ![activity.meanFullReportMs, activity.meanRatingMs].every(amount) || activity.meanRatingMs > activity.meanFullReportMs || !activity.timedReviews && (activity.meanFullReportMs !== 0 || activity.meanRatingMs !== 0) || (activity.reviews || activity.usageRequests || activity.retries || activity.autoApproved ? !count2(activity.since) || activity.since > 864e13 : activity.since !== null)) throw new Error("Invalid lifetime activity totals");
|
|
3668
3709
|
return value;
|
|
3669
3710
|
}
|
|
3711
|
+
function mean(left, leftCount, right, rightCount) {
|
|
3712
|
+
if (!leftCount) return right;
|
|
3713
|
+
if (!rightCount) return left;
|
|
3714
|
+
return left + (right - left) * (rightCount / (leftCount + rightCount));
|
|
3715
|
+
}
|
|
3670
3716
|
function add(left, right) {
|
|
3671
3717
|
return validate({
|
|
3672
3718
|
requests: left.requests + right.requests,
|
|
@@ -3678,7 +3724,17 @@ function add(left, right) {
|
|
|
3678
3724
|
since: left.since === null ? right.since : right.since === null ? left.since : Math.min(left.since, right.since),
|
|
3679
3725
|
safe: left.safe + right.safe,
|
|
3680
3726
|
unsafe: left.unsafe + right.unsafe,
|
|
3681
|
-
ratingsSince: left.ratingsSince === null ? right.ratingsSince : right.ratingsSince === null ? left.ratingsSince : Math.min(left.ratingsSince, right.ratingsSince)
|
|
3727
|
+
ratingsSince: left.ratingsSince === null ? right.ratingsSince : right.ratingsSince === null ? left.ratingsSince : Math.min(left.ratingsSince, right.ratingsSince),
|
|
3728
|
+
activity: {
|
|
3729
|
+
reviews: left.activity.reviews + right.activity.reviews,
|
|
3730
|
+
usageRequests: left.activity.usageRequests + right.activity.usageRequests,
|
|
3731
|
+
retries: left.activity.retries + right.activity.retries,
|
|
3732
|
+
autoApproved: left.activity.autoApproved + right.activity.autoApproved,
|
|
3733
|
+
timedReviews: left.activity.timedReviews + right.activity.timedReviews,
|
|
3734
|
+
meanFullReportMs: mean(left.activity.meanFullReportMs, left.activity.timedReviews, right.activity.meanFullReportMs, right.activity.timedReviews),
|
|
3735
|
+
meanRatingMs: mean(left.activity.meanRatingMs, left.activity.timedReviews, right.activity.meanRatingMs, right.activity.timedReviews),
|
|
3736
|
+
since: left.activity.since === null ? right.activity.since : right.activity.since === null ? left.activity.since : Math.min(left.activity.since, right.activity.since)
|
|
3737
|
+
}
|
|
3682
3738
|
});
|
|
3683
3739
|
}
|
|
3684
3740
|
var LifetimeUsage = class {
|
|
@@ -3707,13 +3763,36 @@ var LifetimeUsage = class {
|
|
|
3707
3763
|
output: usage.output ?? 0,
|
|
3708
3764
|
priced: usage.cost === void 0 ? 0 : 1,
|
|
3709
3765
|
cost: usage.cost ?? 0,
|
|
3710
|
-
since: Date.now()
|
|
3766
|
+
since: Date.now(),
|
|
3767
|
+
activity: { ...emptyActivity(), usageRequests: 1, since: Date.now() }
|
|
3711
3768
|
});
|
|
3712
3769
|
}
|
|
3713
3770
|
/** One accepted final review, independent of POST count or received usage. */
|
|
3714
|
-
recordRating(safe) {
|
|
3771
|
+
recordRating(safe, timing) {
|
|
3715
3772
|
if (typeof safe !== "boolean") return Promise.reject(new Error("Invalid review rating"));
|
|
3716
|
-
|
|
3773
|
+
if (timing && (![timing.fullReportMs, timing.ratingMs].every(amount) || timing.ratingMs > timing.fullReportMs)) {
|
|
3774
|
+
return Promise.reject(new Error("Invalid review timing"));
|
|
3775
|
+
}
|
|
3776
|
+
return this.increment({
|
|
3777
|
+
...empty(),
|
|
3778
|
+
safe: safe ? 1 : 0,
|
|
3779
|
+
unsafe: safe ? 0 : 1,
|
|
3780
|
+
ratingsSince: Date.now(),
|
|
3781
|
+
activity: {
|
|
3782
|
+
...emptyActivity(),
|
|
3783
|
+
reviews: 1,
|
|
3784
|
+
since: Date.now(),
|
|
3785
|
+
timedReviews: timing ? 1 : 0,
|
|
3786
|
+
meanFullReportMs: timing?.fullReportMs ?? 0,
|
|
3787
|
+
meanRatingMs: timing?.ratingMs ?? 0
|
|
3788
|
+
}
|
|
3789
|
+
});
|
|
3790
|
+
}
|
|
3791
|
+
recordRetry() {
|
|
3792
|
+
return this.increment({ ...empty(), activity: { ...emptyActivity(), retries: 1, since: Date.now() } });
|
|
3793
|
+
}
|
|
3794
|
+
recordAutoApproval() {
|
|
3795
|
+
return this.increment({ ...empty(), activity: { ...emptyActivity(), autoApproved: 1, since: Date.now() } });
|
|
3717
3796
|
}
|
|
3718
3797
|
increment(increment) {
|
|
3719
3798
|
this.pending = this.pending.catch(() => {
|
|
@@ -3724,7 +3803,7 @@ var LifetimeUsage = class {
|
|
|
3724
3803
|
try {
|
|
3725
3804
|
const file = await open3(temporary, "wx", 384);
|
|
3726
3805
|
try {
|
|
3727
|
-
await file.writeFile(JSON.stringify({ version:
|
|
3806
|
+
await file.writeFile(JSON.stringify({ version: 4, ...this.local }));
|
|
3728
3807
|
await file.sync();
|
|
3729
3808
|
} finally {
|
|
3730
3809
|
await file.close();
|
|
@@ -3784,7 +3863,7 @@ var LifetimeUsage = class {
|
|
|
3784
3863
|
}
|
|
3785
3864
|
if (size > 1024) throw new Error("Invalid lifetime usage snapshot");
|
|
3786
3865
|
const value = JSON.parse(bytes.subarray(0, size).toString("utf8"));
|
|
3787
|
-
if (
|
|
3866
|
+
if (![1, 2, 3, 4].includes(value?.version)) throw new Error("Unsupported lifetime usage snapshot");
|
|
3788
3867
|
total = add(total, validate({
|
|
3789
3868
|
requests: value.requests,
|
|
3790
3869
|
input: value.input,
|
|
@@ -3793,9 +3872,10 @@ var LifetimeUsage = class {
|
|
|
3793
3872
|
priced: value.priced,
|
|
3794
3873
|
cost: value.cost,
|
|
3795
3874
|
since: value.since,
|
|
3796
|
-
safe: value.version
|
|
3797
|
-
unsafe: value.version
|
|
3798
|
-
ratingsSince: value.version
|
|
3875
|
+
safe: value.version >= 3 ? value.safe : 0,
|
|
3876
|
+
unsafe: value.version >= 3 ? value.unsafe : 0,
|
|
3877
|
+
ratingsSince: value.version >= 3 ? value.ratingsSince : null,
|
|
3878
|
+
activity: value.version === 4 ? value.activity : emptyActivity()
|
|
3799
3879
|
}));
|
|
3800
3880
|
} finally {
|
|
3801
3881
|
await file.close();
|
|
@@ -3812,19 +3892,21 @@ function lifetimeCost(totals) {
|
|
|
3812
3892
|
return uiText.lifetime.cost(totals.cost.toFixed(4), totals.priced < totals.requests);
|
|
3813
3893
|
}
|
|
3814
3894
|
function lifetimeReport(totals) {
|
|
3895
|
+
const reviews = totals.safe + totals.unsafe, activity = totals.activity;
|
|
3896
|
+
const percentage = (count3) => reviews ? (count3 / reviews * 100).toFixed(1) : void 0;
|
|
3897
|
+
const partial = activity.reviews < reviews || activity.usageRequests < totals.requests;
|
|
3815
3898
|
return [
|
|
3816
|
-
|
|
3817
|
-
uiText.lifetime.
|
|
3818
|
-
uiText.lifetime.
|
|
3819
|
-
|
|
3820
|
-
uiText.lifetime.tokenCoverage(totals.tokenRequests, totals.requests),
|
|
3821
|
-
uiText.lifetime.pricingCoverage(totals.priced, totals.requests),
|
|
3822
|
-
...totals.since === null ? [] : [uiText.lifetime.since(new Date(totals.since).toISOString().slice(0, 10))],
|
|
3823
|
-
...totals.ratingsSince === null ? [] : [uiText.lifetime.ratingsSince(new Date(totals.ratingsSince).toISOString().slice(0, 10))],
|
|
3899
|
+
uiText.lifetime.dialogReviews(reviews),
|
|
3900
|
+
uiText.lifetime.dialogRetries(activity.retries, partial),
|
|
3901
|
+
totals.tokenRequests ? uiText.lifetime.dialogTokens(totals.input, totals.output, totals.tokenRequests < totals.requests) : uiText.lifetime.dialogTokensUnavailable,
|
|
3902
|
+
uiText.lifetime.dialogCost(totals.priced ? totals.cost.toFixed(4) : void 0, totals.priced < totals.requests),
|
|
3824
3903
|
"",
|
|
3825
|
-
uiText.lifetime.
|
|
3904
|
+
uiText.lifetime.dialogSafe(totals.safe, percentage(totals.safe)),
|
|
3905
|
+
uiText.lifetime.dialogUnsafe(totals.unsafe, percentage(totals.unsafe)),
|
|
3906
|
+
uiText.lifetime.dialogAutoApproved(activity.autoApproved, percentage(activity.autoApproved), partial),
|
|
3826
3907
|
"",
|
|
3827
|
-
uiText.lifetime.
|
|
3908
|
+
uiText.lifetime.averageFullReport(activity.timedReviews ? (activity.meanFullReportMs / 1e3).toFixed(2) : void 0),
|
|
3909
|
+
uiText.lifetime.averageRating(activity.timedReviews ? (activity.meanRatingMs / 1e3).toFixed(2) : void 0)
|
|
3828
3910
|
].join("\n");
|
|
3829
3911
|
}
|
|
3830
3912
|
|
|
@@ -3910,7 +3992,7 @@ var LifetimeRefresh = class {
|
|
|
3910
3992
|
// src/lifetime-view.tsx
|
|
3911
3993
|
function lifetimeTracker(api) {
|
|
3912
3994
|
const directory = path9.join(api.state.path.state, "opencode-reviewer");
|
|
3913
|
-
const store = new LifetimeUsage(path9.join(directory, "usage-v3"), path9.join(directory, "usage-v2"), path9.join(directory, "usage-v1"));
|
|
3995
|
+
const store = new LifetimeUsage(path9.join(directory, "usage-v4"), path9.join(directory, "usage-v3"), path9.join(directory, "usage-v2"), path9.join(directory, "usage-v1"));
|
|
3914
3996
|
const [totals, setTotals] = createSignal();
|
|
3915
3997
|
const [unavailable, setUnavailable] = createSignal(false);
|
|
3916
3998
|
const refresh = new LifetimeRefresh((signal) => store.totals(signal), (value) => {
|
|
@@ -3951,8 +4033,14 @@ function lifetimeTracker(api) {
|
|
|
3951
4033
|
record: (usage) => {
|
|
3952
4034
|
void store.record(usage).then(() => refresh.refresh(), () => refresh.failed());
|
|
3953
4035
|
},
|
|
3954
|
-
recordRating: (safe) => {
|
|
3955
|
-
void store.recordRating(safe).then(() => refresh.refresh(), () => refresh.failed());
|
|
4036
|
+
recordRating: (safe, timing) => {
|
|
4037
|
+
void store.recordRating(safe, timing).then(() => refresh.refresh(), () => refresh.failed());
|
|
4038
|
+
},
|
|
4039
|
+
recordRetry: () => {
|
|
4040
|
+
void store.recordRetry().then(() => refresh.refresh(), () => refresh.failed());
|
|
4041
|
+
},
|
|
4042
|
+
recordAutoApproval: () => {
|
|
4043
|
+
void store.recordAutoApproval().then(() => refresh.refresh(), () => refresh.failed());
|
|
3956
4044
|
}
|
|
3957
4045
|
};
|
|
3958
4046
|
}
|
|
@@ -6348,7 +6436,7 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6348
6436
|
}, config, configError, signal, onIdentified, files);
|
|
6349
6437
|
if (!evidence) return null;
|
|
6350
6438
|
signal.throwIfAborted();
|
|
6351
|
-
const result = review(evidence, config, signal, void 0, void 0, prompts, (model) => modelPricing(api.state.provider, config.baseURL, model), lifetime.record, onProgress, trace?.forward);
|
|
6439
|
+
const result = review(evidence, config, signal, void 0, void 0, prompts, (model) => modelPricing(api.state.provider, config.baseURL, model), lifetime.record, onProgress, trace?.forward, lifetime.recordRetry);
|
|
6352
6440
|
worker = result;
|
|
6353
6441
|
return result;
|
|
6354
6442
|
});
|
|
@@ -6369,7 +6457,10 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6369
6457
|
once: (request, signal) => measured(traces?.get(request), "approval-reply", () => approval.once(request, signal))
|
|
6370
6458
|
} : approval,
|
|
6371
6459
|
visibleID: () => visibleApproval()
|
|
6372
|
-
}, void 0, modes, (fact) =>
|
|
6460
|
+
}, void 0, modes, (fact) => {
|
|
6461
|
+
if (fact.type === "confirmed" && fact.automatic) lifetime.recordAutoApproval();
|
|
6462
|
+
notifications?.fact(fact);
|
|
6463
|
+
}, lifetime.recordRating);
|
|
6373
6464
|
api.event.on("permission.asked", (event) => controller.asked(event.properties));
|
|
6374
6465
|
api.event.on("permission.replied", (event) => controller.replied(event.properties.requestID));
|
|
6375
6466
|
api.event.on("session.deleted", (event) => {
|