@usepipr/runtime 0.3.6 → 0.3.8
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/dist/{commands-C6wfO-y7.d.mts → commands-C5_GHGxN.d.mts} +59 -1
- package/dist/commands-C5_GHGxN.d.mts.map +1 -0
- package/dist/{commands-Btw_Ummi.mjs → commands-RY37Y0rG.mjs} +375 -43
- package/dist/commands-RY37Y0rG.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/internal/testing.d.mts +1 -1
- package/dist/internal/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/commands-Btw_Ummi.mjs.map +0 -1
- package/dist/commands-C6wfO-y7.d.mts.map +0 -1
|
@@ -272,7 +272,10 @@ const piprConfigSchema = z.strictObject({
|
|
|
272
272
|
providers: z.array(providerConfigSchema).min(1),
|
|
273
273
|
publication: z.strictObject({
|
|
274
274
|
maxInlineComments: z.number().int().min(0).max(50).optional(),
|
|
275
|
-
autoResolve: autoResolveConfigSchema
|
|
275
|
+
autoResolve: autoResolveConfigSchema,
|
|
276
|
+
showHeader: z.boolean().default(true),
|
|
277
|
+
showFooter: z.boolean().default(true),
|
|
278
|
+
showStats: z.boolean().default(true)
|
|
276
279
|
}),
|
|
277
280
|
limits: z.strictObject({
|
|
278
281
|
timeoutSeconds: z.number().int().positive().max(3600).optional(),
|
|
@@ -526,7 +529,7 @@ function compareStableSemver(left, right) {
|
|
|
526
529
|
}
|
|
527
530
|
//#endregion
|
|
528
531
|
//#region src/shared/version.ts
|
|
529
|
-
const runtimeVersion = "0.3.
|
|
532
|
+
const runtimeVersion = "0.3.8";
|
|
530
533
|
//#endregion
|
|
531
534
|
//#region src/config/version-compat.ts
|
|
532
535
|
async function resolveConfigVersionCompatibility(options) {
|
|
@@ -776,7 +779,10 @@ function planToRuntimeSettings(plan, options) {
|
|
|
776
779
|
providers,
|
|
777
780
|
publication: {
|
|
778
781
|
maxInlineComments: plan.publication.maxInlineComments,
|
|
779
|
-
autoResolve: normalizeAutoResolveConfig(plan.publication.autoResolve, defaultProvider.id)
|
|
782
|
+
autoResolve: normalizeAutoResolveConfig(plan.publication.autoResolve, defaultProvider.id),
|
|
783
|
+
showHeader: plan.publication.showHeader ?? true,
|
|
784
|
+
showFooter: plan.publication.showFooter ?? true,
|
|
785
|
+
showStats: plan.publication.showStats ?? true
|
|
780
786
|
},
|
|
781
787
|
limits: plan.limits
|
|
782
788
|
},
|
|
@@ -3544,8 +3550,8 @@ function isOfficialInitRecipeId(recipe) {
|
|
|
3544
3550
|
//#endregion
|
|
3545
3551
|
//#region src/config/init.ts
|
|
3546
3552
|
const supportedOfficialInitAdapters = ["github"];
|
|
3547
|
-
const defaultWorkflowActionRef = "somus/pipr@v0.3.
|
|
3548
|
-
const defaultSdkVersion = "0.3.
|
|
3553
|
+
const defaultWorkflowActionRef = "somus/pipr@v0.3.8";
|
|
3554
|
+
const defaultSdkVersion = "0.3.8";
|
|
3549
3555
|
function resolveOfficialInitAdapters(adapters) {
|
|
3550
3556
|
if (adapters === void 0) return [...supportedOfficialInitAdapters];
|
|
3551
3557
|
if (adapters.length === 0) return [];
|
|
@@ -4746,6 +4752,24 @@ const ignoredWorkspacePaths = /* @__PURE__ */ new Set([
|
|
|
4746
4752
|
".fallow",
|
|
4747
4753
|
"coverage"
|
|
4748
4754
|
]);
|
|
4755
|
+
const eventRecordSchema = z.record(z.string(), z.unknown());
|
|
4756
|
+
const tokenCountSchema = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
4757
|
+
const assistantMessageEventSchema = z.looseObject({
|
|
4758
|
+
type: z.literal("message_end"),
|
|
4759
|
+
message: z.looseObject({
|
|
4760
|
+
role: z.literal("assistant"),
|
|
4761
|
+
model: z.string().min(1).optional(),
|
|
4762
|
+
responseModel: z.string().min(1).optional()
|
|
4763
|
+
})
|
|
4764
|
+
});
|
|
4765
|
+
const assistantUsageMessageSchema = z.looseObject({
|
|
4766
|
+
role: z.literal("assistant"),
|
|
4767
|
+
usage: z.looseObject({
|
|
4768
|
+
input: tokenCountSchema,
|
|
4769
|
+
output: tokenCountSchema,
|
|
4770
|
+
cost: z.looseObject({ total: z.number().nonnegative() })
|
|
4771
|
+
})
|
|
4772
|
+
});
|
|
4749
4773
|
async function runPi(options) {
|
|
4750
4774
|
const started = Date.now();
|
|
4751
4775
|
const sandbox = await createPiRunSandbox(options.workspace);
|
|
@@ -4768,10 +4792,13 @@ async function runPi(options) {
|
|
|
4768
4792
|
started,
|
|
4769
4793
|
timeoutSeconds: options.timeoutSeconds
|
|
4770
4794
|
});
|
|
4771
|
-
|
|
4795
|
+
const events = parsePiJsonEvents(result.stdout);
|
|
4796
|
+
return {
|
|
4772
4797
|
...result,
|
|
4773
|
-
|
|
4774
|
-
|
|
4798
|
+
...events?.models.length ? { models: events.models } : {},
|
|
4799
|
+
...events?.usage ? { usage: events.usage } : {},
|
|
4800
|
+
stdout: result.exitCode === 0 ? events?.assistantText ?? result.stdout : result.stdout
|
|
4801
|
+
};
|
|
4775
4802
|
} finally {
|
|
4776
4803
|
await preparedTools?.custom?.close();
|
|
4777
4804
|
await chmodRecursive(sandbox.root, 493);
|
|
@@ -4893,19 +4920,69 @@ async function chmodRecursive(target, mode) {
|
|
|
4893
4920
|
const entries = await readdir(target, { withFileTypes: true });
|
|
4894
4921
|
for (const entry of entries) await chmodRecursive(path.join(target, entry.name), mode);
|
|
4895
4922
|
}
|
|
4896
|
-
function
|
|
4923
|
+
function parsePiJsonEvents(stdout) {
|
|
4897
4924
|
const events = [];
|
|
4898
4925
|
for (const line of stdout.split(/\r?\n/).map((item) => item.trim()).filter(Boolean)) try {
|
|
4899
4926
|
const value = JSON.parse(line);
|
|
4900
|
-
|
|
4901
|
-
|
|
4927
|
+
const parsed = eventRecordSchema.safeParse(value);
|
|
4928
|
+
if (!parsed.success) return;
|
|
4929
|
+
events.push(parsed.data);
|
|
4902
4930
|
} catch {
|
|
4903
4931
|
return;
|
|
4904
4932
|
}
|
|
4905
4933
|
if (!events.some((event) => typeof event.type === "string")) return;
|
|
4906
|
-
let
|
|
4907
|
-
for (const event of events)
|
|
4908
|
-
|
|
4934
|
+
let assistantText;
|
|
4935
|
+
for (const event of events) assistantText = assistantTextFromEvent(event) ?? assistantText;
|
|
4936
|
+
const assistantMessages = assistantMessagesFromEvents(events);
|
|
4937
|
+
const models = assistantModels(assistantMessages);
|
|
4938
|
+
const usage = assistantUsage(assistantMessages);
|
|
4939
|
+
return {
|
|
4940
|
+
...assistantText !== void 0 ? { assistantText } : {},
|
|
4941
|
+
models,
|
|
4942
|
+
...usage ? { usage } : {}
|
|
4943
|
+
};
|
|
4944
|
+
}
|
|
4945
|
+
function assistantMessagesFromEvents(events) {
|
|
4946
|
+
return events.flatMap((event) => {
|
|
4947
|
+
const parsed = assistantMessageEventSchema.safeParse(event);
|
|
4948
|
+
return parsed.success ? [parsed.data.message] : [];
|
|
4949
|
+
});
|
|
4950
|
+
}
|
|
4951
|
+
function assistantModels(messages) {
|
|
4952
|
+
const models = [];
|
|
4953
|
+
for (const message of messages) {
|
|
4954
|
+
const model = message.responseModel ?? message.model;
|
|
4955
|
+
if (model && !models.includes(model)) models.push(model);
|
|
4956
|
+
}
|
|
4957
|
+
return models;
|
|
4958
|
+
}
|
|
4959
|
+
function assistantUsage(messages) {
|
|
4960
|
+
const usageMessages = messages.flatMap((message) => {
|
|
4961
|
+
const parsed = assistantUsageMessageSchema.safeParse(message);
|
|
4962
|
+
return parsed.success ? [parsed.data] : [];
|
|
4963
|
+
});
|
|
4964
|
+
if (usageMessages.length === 0) return;
|
|
4965
|
+
let inputTokens = 0;
|
|
4966
|
+
let outputTokens = 0;
|
|
4967
|
+
let costUsd = 0;
|
|
4968
|
+
let partial = usageMessages.length !== messages.length;
|
|
4969
|
+
for (const message of usageMessages) {
|
|
4970
|
+
const nextInputTokens = inputTokens + message.usage.input;
|
|
4971
|
+
if (Number.isSafeInteger(nextInputTokens)) inputTokens = nextInputTokens;
|
|
4972
|
+
else partial = true;
|
|
4973
|
+
const nextOutputTokens = outputTokens + message.usage.output;
|
|
4974
|
+
if (Number.isSafeInteger(nextOutputTokens)) outputTokens = nextOutputTokens;
|
|
4975
|
+
else partial = true;
|
|
4976
|
+
const nextCostUsd = costUsd + message.usage.cost.total;
|
|
4977
|
+
if (Number.isFinite(nextCostUsd)) costUsd = nextCostUsd;
|
|
4978
|
+
else partial = true;
|
|
4979
|
+
}
|
|
4980
|
+
return {
|
|
4981
|
+
status: partial ? "partial" : "complete",
|
|
4982
|
+
inputTokens,
|
|
4983
|
+
outputTokens,
|
|
4984
|
+
costUsd
|
|
4985
|
+
};
|
|
4909
4986
|
}
|
|
4910
4987
|
function assistantTextFromEvent(event) {
|
|
4911
4988
|
if (event.type === "message_end" || event.type === "turn_end") return assistantMessageText(event.message);
|
|
@@ -5521,16 +5598,27 @@ async function runPiForPrompt(options, provider, prompt) {
|
|
|
5521
5598
|
const customTools = customToolsForRun(options);
|
|
5522
5599
|
const timeoutSeconds = promptTimeoutSeconds(options);
|
|
5523
5600
|
logPiStart(options, provider, prompt, builtinTools, runtimeTools, customTools);
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5601
|
+
let result;
|
|
5602
|
+
try {
|
|
5603
|
+
result = await (options.runtime.piRunner ?? runPi)({
|
|
5604
|
+
workspace: options.runtime.workspace,
|
|
5605
|
+
provider,
|
|
5606
|
+
prompt,
|
|
5607
|
+
env: options.runtime.env,
|
|
5608
|
+
piExecutable: options.runtime.piExecutable,
|
|
5609
|
+
builtinTools,
|
|
5610
|
+
runtimeTools,
|
|
5611
|
+
customTools,
|
|
5612
|
+
timeoutSeconds
|
|
5613
|
+
});
|
|
5614
|
+
} catch (error) {
|
|
5615
|
+
options.runtime.piRunSink?.({ models: [provider.model] });
|
|
5616
|
+
throw error;
|
|
5617
|
+
}
|
|
5618
|
+
const reportedModels = result.models?.map((model) => model.trim()).filter(Boolean);
|
|
5619
|
+
options.runtime.piRunSink?.({
|
|
5620
|
+
models: reportedModels?.length ? reportedModels : [provider.model],
|
|
5621
|
+
...result.usage ? { usage: result.usage } : {}
|
|
5534
5622
|
});
|
|
5535
5623
|
logPiResult(options, provider, result, timeoutSeconds);
|
|
5536
5624
|
assertSuccessfulPiResult(result, options.runtime.log);
|
|
@@ -5674,8 +5762,63 @@ const mainCommentTitles = /* @__PURE__ */ new Set([
|
|
|
5674
5762
|
"# Pipr Review",
|
|
5675
5763
|
mainCommentTitle
|
|
5676
5764
|
]);
|
|
5765
|
+
const mainCommentHeaderHiddenMarker = "<!-- pipr:header:hidden -->";
|
|
5766
|
+
const mainCommentFooterHiddenMarker = "<!-- pipr:footer:hidden -->";
|
|
5767
|
+
const reviewStatsStartMarker = "<!-- pipr:stats:start -->";
|
|
5768
|
+
const reviewStatsEndMarker = "<!-- pipr:stats:end -->";
|
|
5769
|
+
const reviewStatsHiddenMarker = "<!-- pipr:stats:hidden -->";
|
|
5677
5770
|
const escapedRepositoryUrl = piprRepositoryUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5678
5771
|
const mainCommentAttributionPattern = new RegExp(`^<sub>Review generated by \\[Pipr\\]\\(${escapedRepositoryUrl}\\) for commit \`[^\`]+\`\\.(?: Config SDK \\d+\\.\\d+\\.\\d+ is behind (?:Pipr \\d+\\.\\d+\\.\\d+|\\[Pipr \\d+\\.\\d+\\.\\d+\\]\\(${escapedRepositoryUrl}/releases/tag/v\\d+\\.\\d+\\.\\d+\\))\\.)?</sub>$`);
|
|
5772
|
+
const maxReviewStatsModelLength = 200;
|
|
5773
|
+
const credentialLikeModelPattern = /(?:(?:AKIA|ASIA)[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|(?:sk|rk)_live_[A-Za-z0-9]{16,}|sk-(?:proj-)?[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{16,}|glpat-[A-Za-z0-9_-]{16,}|npm_[A-Za-z0-9]{16,}|xox[baprs]-[A-Za-z0-9-]{16,})/i;
|
|
5774
|
+
const highEntropyModelSegmentPattern = /[A-Za-z0-9]{24,}/;
|
|
5775
|
+
const reviewStatsModelSchema = z.string().min(1).max(maxReviewStatsModelLength).transform((model) => sanitizeReviewStatsModel(model) ?? "[invalid model]");
|
|
5776
|
+
const reviewStatsSchema = z.strictObject({
|
|
5777
|
+
models: z.array(reviewStatsModelSchema).min(1).max(20),
|
|
5778
|
+
agentRuns: z.number().int().positive(),
|
|
5779
|
+
durationMs: z.number().int().nonnegative(),
|
|
5780
|
+
inputTokens: z.number().int().nonnegative(),
|
|
5781
|
+
outputTokens: z.number().int().nonnegative(),
|
|
5782
|
+
costUsd: z.number().nonnegative(),
|
|
5783
|
+
usageStatus: z.enum([
|
|
5784
|
+
"complete",
|
|
5785
|
+
"partial",
|
|
5786
|
+
"unavailable"
|
|
5787
|
+
])
|
|
5788
|
+
});
|
|
5789
|
+
function accumulateReviewStats(prior, current) {
|
|
5790
|
+
if (!prior) return current;
|
|
5791
|
+
if (!current) return prior;
|
|
5792
|
+
const inputTokens = addUsageTotal(prior.inputTokens, current.inputTokens, Number.isSafeInteger);
|
|
5793
|
+
const outputTokens = addUsageTotal(prior.outputTokens, current.outputTokens, Number.isSafeInteger);
|
|
5794
|
+
const costUsd = addUsageTotal(prior.costUsd, current.costUsd, Number.isFinite);
|
|
5795
|
+
const usageStatus = inputTokens.complete && outputTokens.complete && costUsd.complete && prior.usageStatus === current.usageStatus ? prior.usageStatus : "partial";
|
|
5796
|
+
return {
|
|
5797
|
+
models: [.../* @__PURE__ */ new Set([...prior.models, ...current.models])].slice(0, 20),
|
|
5798
|
+
agentRuns: Math.min(Number.MAX_SAFE_INTEGER, prior.agentRuns + current.agentRuns),
|
|
5799
|
+
durationMs: Math.min(Number.MAX_SAFE_INTEGER, prior.durationMs + current.durationMs),
|
|
5800
|
+
inputTokens: inputTokens.total,
|
|
5801
|
+
outputTokens: outputTokens.total,
|
|
5802
|
+
costUsd: costUsd.total,
|
|
5803
|
+
usageStatus
|
|
5804
|
+
};
|
|
5805
|
+
}
|
|
5806
|
+
function addUsageTotal(prior, current, isValid) {
|
|
5807
|
+
const total = prior + current;
|
|
5808
|
+
return isValid(total) && total >= 0 ? {
|
|
5809
|
+
total,
|
|
5810
|
+
complete: true
|
|
5811
|
+
} : {
|
|
5812
|
+
total: prior,
|
|
5813
|
+
complete: false
|
|
5814
|
+
};
|
|
5815
|
+
}
|
|
5816
|
+
function sanitizeReviewStatsModel(model) {
|
|
5817
|
+
const normalized = model.replace(/\s+/g, " ").trim();
|
|
5818
|
+
if (credentialLikeModelPattern.test(normalized) || highEntropyModelSegmentPattern.test(normalized)) return "[redacted credential]";
|
|
5819
|
+
const sanitized = redactPotentialSecrets(normalized);
|
|
5820
|
+
return sanitized ? sanitized.slice(0, maxReviewStatsModelLength) : void 0;
|
|
5821
|
+
}
|
|
5679
5822
|
//#endregion
|
|
5680
5823
|
//#region src/review/prior-state.ts
|
|
5681
5824
|
const mainCommentMarker = "pipr:main-comment";
|
|
@@ -5701,7 +5844,8 @@ const priorReviewStateSchema = z.strictObject({
|
|
|
5701
5844
|
version: z.literal(1),
|
|
5702
5845
|
reviewedHeadSha: z.string().min(1),
|
|
5703
5846
|
selectedTasks: z.array(z.string().min(1)),
|
|
5704
|
-
findings: z.array(priorFindingRecordSchema)
|
|
5847
|
+
findings: z.array(priorFindingRecordSchema),
|
|
5848
|
+
stats: reviewStatsSchema.optional()
|
|
5705
5849
|
});
|
|
5706
5850
|
function buildPriorReviewState(options) {
|
|
5707
5851
|
const scopedPriorState = priorReviewStateForSelectedTasks(options.priorState, options.selectedTasks);
|
|
@@ -5709,6 +5853,7 @@ function buildPriorReviewState(options) {
|
|
|
5709
5853
|
const nextFindings = /* @__PURE__ */ new Map();
|
|
5710
5854
|
const currentFindingIds = /* @__PURE__ */ new Set();
|
|
5711
5855
|
const usedPriorIds = /* @__PURE__ */ new Set();
|
|
5856
|
+
const stats = accumulateReviewStats(scopedPriorState?.stats, options.stats);
|
|
5712
5857
|
for (const finding of options.findings) {
|
|
5713
5858
|
const id = selectFindingId({
|
|
5714
5859
|
finding,
|
|
@@ -5741,7 +5886,8 @@ function buildPriorReviewState(options) {
|
|
|
5741
5886
|
version: 1,
|
|
5742
5887
|
reviewedHeadSha: options.reviewedHeadSha,
|
|
5743
5888
|
selectedTasks: options.selectedTasks,
|
|
5744
|
-
findings: cappedFindings([...nextFindings.values()], currentFindingIds)
|
|
5889
|
+
findings: cappedFindings([...nextFindings.values()], currentFindingIds),
|
|
5890
|
+
...stats ? { stats } : {}
|
|
5745
5891
|
};
|
|
5746
5892
|
}
|
|
5747
5893
|
function resolvePriorFindings(state, findingIds) {
|
|
@@ -5990,7 +6136,8 @@ const publicationMetadataSchema = z.strictObject({
|
|
|
5990
6136
|
failedTasks: z.array(z.string().min(1)),
|
|
5991
6137
|
validFindings: z.number().int().min(0),
|
|
5992
6138
|
droppedFindings: z.number().int().min(0),
|
|
5993
|
-
cappedInlineFindings: z.number().int().min(0)
|
|
6139
|
+
cappedInlineFindings: z.number().int().min(0),
|
|
6140
|
+
stats: reviewStatsSchema.optional()
|
|
5994
6141
|
});
|
|
5995
6142
|
const publicationPlanSchema = z.strictObject({
|
|
5996
6143
|
mainComment: z.string().min(1),
|
|
@@ -6017,7 +6164,10 @@ function buildPublicationPlan(options) {
|
|
|
6017
6164
|
event: options.event,
|
|
6018
6165
|
reviewState,
|
|
6019
6166
|
main: options.main,
|
|
6020
|
-
metadata
|
|
6167
|
+
metadata,
|
|
6168
|
+
showHeader: options.showHeader ?? true,
|
|
6169
|
+
showFooter: options.showFooter ?? true,
|
|
6170
|
+
showStats: options.showStats ?? true
|
|
6021
6171
|
}),
|
|
6022
6172
|
mainMarker: mainCommentMarker,
|
|
6023
6173
|
changeNumber: options.event.change.number,
|
|
@@ -6101,15 +6251,59 @@ function renderMainComment(options) {
|
|
|
6101
6251
|
reviewState: options.reviewState
|
|
6102
6252
|
}),
|
|
6103
6253
|
"",
|
|
6104
|
-
|
|
6105
|
-
"",
|
|
6254
|
+
...!options.showHeader ? [mainCommentHeaderHiddenMarker, ""] : [],
|
|
6255
|
+
...options.showHeader ? [mainCommentTitle, ""] : [],
|
|
6106
6256
|
...options.metadata.validFindings > 0 ? [`**Findings:** ${options.metadata.validFindings}`, ""] : [],
|
|
6107
6257
|
redactPotentialSecrets(options.main),
|
|
6108
6258
|
"",
|
|
6109
|
-
|
|
6110
|
-
""
|
|
6259
|
+
...!options.showStats || !options.metadata.stats ? [reviewStatsHiddenMarker, ""] : [],
|
|
6260
|
+
...options.showStats && options.metadata.stats ? [renderReviewStats(options.metadata.stats), ""] : [],
|
|
6261
|
+
...options.showFooter ? [renderMainCommentAttribution(options.metadata), ""] : [mainCommentFooterHiddenMarker, ""]
|
|
6111
6262
|
].join("\n");
|
|
6112
6263
|
}
|
|
6264
|
+
function renderReviewStats(stats) {
|
|
6265
|
+
const usageSuffix = stats.usageStatus === "partial" ? " (reported)" : "";
|
|
6266
|
+
const usageUnavailable = stats.usageStatus === "unavailable";
|
|
6267
|
+
return [
|
|
6268
|
+
reviewStatsStartMarker,
|
|
6269
|
+
"<details>",
|
|
6270
|
+
"<summary>Review stats</summary>",
|
|
6271
|
+
"",
|
|
6272
|
+
"| Metric | Total |",
|
|
6273
|
+
"| --- | ---: |",
|
|
6274
|
+
`| Models | ${stats.models.map(formatModel).join(", ")} |`,
|
|
6275
|
+
`| Agent runs | ${stats.agentRuns} |`,
|
|
6276
|
+
`| Elapsed | ${formatDuration(stats.durationMs)} |`,
|
|
6277
|
+
`| Input tokens | ${usageUnavailable ? "Unavailable" : `${formatInteger(stats.inputTokens)}${usageSuffix}`} |`,
|
|
6278
|
+
`| Output tokens | ${usageUnavailable ? "Unavailable" : `${formatInteger(stats.outputTokens)}${usageSuffix}`} |`,
|
|
6279
|
+
`| Cost (USD) | ${usageUnavailable ? "Unavailable" : `${formatCost(stats.costUsd)}${usageSuffix}`} |`,
|
|
6280
|
+
"",
|
|
6281
|
+
"</details>",
|
|
6282
|
+
reviewStatsEndMarker
|
|
6283
|
+
].join("\n");
|
|
6284
|
+
}
|
|
6285
|
+
function formatModel(model) {
|
|
6286
|
+
return `<code>${model.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\|/g, "|")}</code>`;
|
|
6287
|
+
}
|
|
6288
|
+
function formatInteger(value) {
|
|
6289
|
+
return value.toLocaleString("en-US");
|
|
6290
|
+
}
|
|
6291
|
+
function formatDuration(durationMs) {
|
|
6292
|
+
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
6293
|
+
const totalSeconds = durationMs / 1e3;
|
|
6294
|
+
if (totalSeconds < 60) return `${formatTenths(totalSeconds)}s`;
|
|
6295
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
6296
|
+
return `${minutes}m ${formatTenths(totalSeconds - minutes * 60)}s`;
|
|
6297
|
+
}
|
|
6298
|
+
function formatTenths(value) {
|
|
6299
|
+
return value.toFixed(1).replace(/\.0$/, "");
|
|
6300
|
+
}
|
|
6301
|
+
function formatCost(costUsd) {
|
|
6302
|
+
if (costUsd === 0) return "$0.00";
|
|
6303
|
+
if (costUsd < 1e-4) return `$${costUsd.toFixed(6)}`;
|
|
6304
|
+
if (costUsd < .01) return `$${costUsd.toFixed(4)}`;
|
|
6305
|
+
return `$${costUsd.toFixed(2)}`;
|
|
6306
|
+
}
|
|
6113
6307
|
function renderMainCommentAttribution(metadata) {
|
|
6114
6308
|
const configNotice = configVersionNotice(metadata);
|
|
6115
6309
|
return `<sub>Review generated by [Pipr](${piprRepositoryUrl}) for commit \`${metadata.reviewedHeadSha.slice(0, 7)}\`.${configNotice}</sub>`;
|
|
@@ -6155,7 +6349,8 @@ function buildCommentPublishingPlan(options) {
|
|
|
6155
6349
|
priorState: options.priorReviewState,
|
|
6156
6350
|
findings: publishableInlineFindings.map((item) => item.finding),
|
|
6157
6351
|
reviewedHeadSha: options.event.change.head.sha,
|
|
6158
|
-
selectedTasks: options.metadata.selectedTasks
|
|
6352
|
+
selectedTasks: options.metadata.selectedTasks,
|
|
6353
|
+
stats: options.metadata.stats
|
|
6159
6354
|
});
|
|
6160
6355
|
const inlineCommentDrafts = prepareInlinePublicationItemsForPublishableFindings({
|
|
6161
6356
|
publishableFindings: publishableInlineFindings,
|
|
@@ -6167,7 +6362,13 @@ function buildCommentPublishingPlan(options) {
|
|
|
6167
6362
|
main: options.main,
|
|
6168
6363
|
inlineItems: inlineCommentDrafts,
|
|
6169
6364
|
maxInlineComments: options.maxInlineComments,
|
|
6170
|
-
|
|
6365
|
+
showHeader: options.showHeader,
|
|
6366
|
+
showFooter: options.showFooter,
|
|
6367
|
+
showStats: options.showStats,
|
|
6368
|
+
metadata: {
|
|
6369
|
+
...options.metadata,
|
|
6370
|
+
...reviewState.stats ? { stats: reviewState.stats } : {}
|
|
6371
|
+
},
|
|
6171
6372
|
reviewState,
|
|
6172
6373
|
threadActions: options.threadActions
|
|
6173
6374
|
});
|
|
@@ -6266,7 +6467,8 @@ async function runInternalVerifier(options) {
|
|
|
6266
6467
|
piExecutable: options.piExecutable,
|
|
6267
6468
|
piRunner: options.piRunner,
|
|
6268
6469
|
runId: options.runId,
|
|
6269
|
-
log: options.log
|
|
6470
|
+
log: options.log,
|
|
6471
|
+
...options.piRunSink ? { piRunSink: options.piRunSink } : {}
|
|
6270
6472
|
}
|
|
6271
6473
|
});
|
|
6272
6474
|
return applyVerifierOutput(options, candidates, verifierOutputSchema.parse(result.value), result.providerModels);
|
|
@@ -6438,6 +6640,20 @@ function boundedVerifierText(value) {
|
|
|
6438
6640
|
//#endregion
|
|
6439
6641
|
//#region src/review/task/task-output.ts
|
|
6440
6642
|
const agentInlineFindingsOutputSchema = z.custom((value) => z.looseObject({ inlineFindings: z.array(reviewFindingSchema) }).safeParse(value).success);
|
|
6643
|
+
const generatedReviewStatsShape = [
|
|
6644
|
+
/^$/,
|
|
6645
|
+
/^\| Metric \| Total \|$/,
|
|
6646
|
+
/^\| --- \| ---: \|$/,
|
|
6647
|
+
/^\| Models \| .+ \|$/,
|
|
6648
|
+
/^\| Agent runs \| \d+ \|$/,
|
|
6649
|
+
/^\| Elapsed \| .+ \|$/,
|
|
6650
|
+
/^\| Input tokens \| (?:Unavailable|[\d,]+(?: \(reported\))?) \|$/,
|
|
6651
|
+
/^\| Output tokens \| (?:Unavailable|[\d,]+(?: \(reported\))?) \|$/,
|
|
6652
|
+
/^\| Cost \(USD\) \| (?:Unavailable|\$\d+(?:\.\d+)?(?:e[+-]?\d+)?(?: \(reported\))?) \|$/,
|
|
6653
|
+
/^$/,
|
|
6654
|
+
/^<\/details>$/,
|
|
6655
|
+
/^<!-- pipr:stats:end -->$/
|
|
6656
|
+
];
|
|
6441
6657
|
function createOutputState() {
|
|
6442
6658
|
return {
|
|
6443
6659
|
findings: [],
|
|
@@ -6457,6 +6673,66 @@ function mergeTaskOutputs(results) {
|
|
|
6457
6673
|
}
|
|
6458
6674
|
return merged;
|
|
6459
6675
|
}
|
|
6676
|
+
function reviewStatsForRuns(runs, durationMs) {
|
|
6677
|
+
if (runs.length === 0) return;
|
|
6678
|
+
const usage = aggregateReviewUsage(runs);
|
|
6679
|
+
return {
|
|
6680
|
+
models: collectReviewModels(runs),
|
|
6681
|
+
agentRuns: runs.length,
|
|
6682
|
+
durationMs,
|
|
6683
|
+
inputTokens: usage.inputTokens,
|
|
6684
|
+
outputTokens: usage.outputTokens,
|
|
6685
|
+
costUsd: usage.costUsd,
|
|
6686
|
+
usageStatus: usage.status
|
|
6687
|
+
};
|
|
6688
|
+
}
|
|
6689
|
+
function collectReviewModels(runs) {
|
|
6690
|
+
const models = [];
|
|
6691
|
+
for (const model of runs.flatMap((run) => run.models)) {
|
|
6692
|
+
const sanitized = sanitizeReviewStatsModel(model);
|
|
6693
|
+
if (sanitized && models.length < 20 && !models.includes(sanitized)) models.push(sanitized);
|
|
6694
|
+
}
|
|
6695
|
+
return models.length > 0 ? models : ["[invalid model]"];
|
|
6696
|
+
}
|
|
6697
|
+
function aggregateReviewUsage(runs) {
|
|
6698
|
+
let inputTokens = 0;
|
|
6699
|
+
let outputTokens = 0;
|
|
6700
|
+
let costUsd = 0;
|
|
6701
|
+
let reportedRuns = 0;
|
|
6702
|
+
let partialUsage = false;
|
|
6703
|
+
for (const run of runs) {
|
|
6704
|
+
if (!run.usage) continue;
|
|
6705
|
+
reportedRuns += 1;
|
|
6706
|
+
const input = addReportedUsage(inputTokens, run.usage.inputTokens, Number.isSafeInteger);
|
|
6707
|
+
const output = addReportedUsage(outputTokens, run.usage.outputTokens, Number.isSafeInteger);
|
|
6708
|
+
const cost = addReportedUsage(costUsd, run.usage.costUsd, Number.isFinite);
|
|
6709
|
+
inputTokens = input.total;
|
|
6710
|
+
outputTokens = output.total;
|
|
6711
|
+
costUsd = cost.total;
|
|
6712
|
+
const sumsComplete = [
|
|
6713
|
+
input,
|
|
6714
|
+
output,
|
|
6715
|
+
cost
|
|
6716
|
+
].every((sum) => sum.complete);
|
|
6717
|
+
partialUsage ||= run.usage.status === "partial" || !sumsComplete;
|
|
6718
|
+
}
|
|
6719
|
+
return {
|
|
6720
|
+
inputTokens,
|
|
6721
|
+
outputTokens,
|
|
6722
|
+
costUsd,
|
|
6723
|
+
status: reportedRuns === 0 ? "unavailable" : reportedRuns < runs.length || partialUsage ? "partial" : "complete"
|
|
6724
|
+
};
|
|
6725
|
+
}
|
|
6726
|
+
function addReportedUsage(current, reported, isValid) {
|
|
6727
|
+
const next = current + reported;
|
|
6728
|
+
return isValid(next) ? {
|
|
6729
|
+
total: next,
|
|
6730
|
+
complete: true
|
|
6731
|
+
} : {
|
|
6732
|
+
total: current,
|
|
6733
|
+
complete: false
|
|
6734
|
+
};
|
|
6735
|
+
}
|
|
6460
6736
|
function mergeCommentContribution(merged, comment) {
|
|
6461
6737
|
if (!comment) return;
|
|
6462
6738
|
assertOutputContributionAllowed(merged, "comment", comment.taskName, (existing, next) => `ctx.comment(...) may be called once per selected run; received comments from '${existing}' and '${next}'`);
|
|
@@ -6534,12 +6810,52 @@ function priorReviewForTask(priorMainComment, priorReviewState) {
|
|
|
6534
6810
|
};
|
|
6535
6811
|
}
|
|
6536
6812
|
function visibleMainComment(body) {
|
|
6537
|
-
const
|
|
6813
|
+
const sourceLines = body.split("\n");
|
|
6814
|
+
const envelope = parseGeneratedMainCommentEnvelope(sourceLines);
|
|
6815
|
+
const lines = sourceLines.filter((_line, index) => {
|
|
6816
|
+
return !(envelope.statsRange && index >= envelope.statsRange.start && index <= envelope.statsRange.end) && index !== envelope.mainMarkerIndex && index !== envelope.headerMarkerIndex && index !== envelope.statsMarkerIndex && index !== envelope.footerIndex;
|
|
6817
|
+
});
|
|
6538
6818
|
while (lines[0] === "") lines.shift();
|
|
6539
|
-
if (lines[0] && mainCommentTitles.has(lines[0])) lines.shift();
|
|
6819
|
+
if (envelope.headerMarkerIndex < 0 && lines[0] && mainCommentTitles.has(lines[0])) lines.shift();
|
|
6540
6820
|
while (lines[0] === "") lines.shift();
|
|
6541
6821
|
return lines.join("\n").trim();
|
|
6542
6822
|
}
|
|
6823
|
+
function parseGeneratedMainCommentEnvelope(lines) {
|
|
6824
|
+
const mainMarkerIndex = lines.findIndex((line) => line.startsWith("<!-- pipr:main-comment "));
|
|
6825
|
+
const headerCandidateOffset = lines.slice(mainMarkerIndex + 1).findIndex((line) => line !== "");
|
|
6826
|
+
const headerCandidateIndex = mainMarkerIndex + 1 + headerCandidateOffset;
|
|
6827
|
+
const headerMarkerIndex = mainMarkerIndex >= 0 && headerCandidateOffset >= 0 && lines[headerCandidateIndex] === "<!-- pipr:header:hidden -->" ? headerCandidateIndex : -1;
|
|
6828
|
+
const lastLineIndex = lines.findLastIndex((line) => line !== "");
|
|
6829
|
+
const lastLine = lines[lastLineIndex] ?? "";
|
|
6830
|
+
const footerIndex = lastLine === "<!-- pipr:footer:hidden -->" || mainCommentAttributionPattern.test(lastLine) ? lastLineIndex : -1;
|
|
6831
|
+
const lastContentIndex = lines.slice(0, footerIndex < 0 ? lines.length : footerIndex).findLastIndex((line) => line !== "");
|
|
6832
|
+
return {
|
|
6833
|
+
mainMarkerIndex,
|
|
6834
|
+
headerMarkerIndex,
|
|
6835
|
+
statsMarkerIndex: lines[lastContentIndex] === "<!-- pipr:stats:hidden -->" ? lastContentIndex : -1,
|
|
6836
|
+
statsRange: generatedReviewStatsRange(lines, footerIndex),
|
|
6837
|
+
footerIndex
|
|
6838
|
+
};
|
|
6839
|
+
}
|
|
6840
|
+
function generatedReviewStatsRange(lines, generatedFooterIndex) {
|
|
6841
|
+
const end = lines.slice(0, generatedFooterIndex < 0 ? lines.length : generatedFooterIndex).findLastIndex((line) => line !== "");
|
|
6842
|
+
if (end < 0) return;
|
|
6843
|
+
if (lines[end] !== "<!-- pipr:stats:end -->") return;
|
|
6844
|
+
if (lines[end - 1] !== "</details>") return;
|
|
6845
|
+
const start = lines.lastIndexOf(reviewStatsStartMarker, end - 2);
|
|
6846
|
+
if (start < 0) return;
|
|
6847
|
+
if (lines[start + 1] !== "<details>") return;
|
|
6848
|
+
if (lines[start + 2] !== "<summary>Review stats</summary>") return;
|
|
6849
|
+
if (!matchesGeneratedReviewStatsShape(lines, start, end)) return;
|
|
6850
|
+
return {
|
|
6851
|
+
start,
|
|
6852
|
+
end
|
|
6853
|
+
};
|
|
6854
|
+
}
|
|
6855
|
+
function matchesGeneratedReviewStatsShape(lines, start, end) {
|
|
6856
|
+
const generatedShape = lines.slice(start + 3, end + 1);
|
|
6857
|
+
return generatedShape.length === generatedReviewStatsShape.length && generatedReviewStatsShape.every((pattern, index) => pattern.test(generatedShape[index] ?? ""));
|
|
6858
|
+
}
|
|
6543
6859
|
function collectInlineFindings(state, findings) {
|
|
6544
6860
|
if (!findings) return;
|
|
6545
6861
|
const arrayScope = state.findingScopes.get(findings);
|
|
@@ -6563,6 +6879,7 @@ function collectedReview(output, summaryBody) {
|
|
|
6563
6879
|
//#region src/review/task/task-runtime.ts
|
|
6564
6880
|
const genericTaskFailureSummary = "Task failed; see logs for details.";
|
|
6565
6881
|
async function runTaskRuntime(options) {
|
|
6882
|
+
const runtimeStarted = Date.now();
|
|
6566
6883
|
const config = parsePiprConfig(options.config);
|
|
6567
6884
|
const provider = options.providerOverride ? parseProviderConfig(options.providerOverride) : resolveProvider(config, config.defaultProvider);
|
|
6568
6885
|
const diffManifest = parseDiffManifest((options.diffManifestBuilder ?? buildDiffManifest)({
|
|
@@ -6615,11 +6932,15 @@ async function runTaskRuntime(options) {
|
|
|
6615
6932
|
const loadedPriorReviewState = options.priorReviewState ?? await options.loadPriorReviewState?.();
|
|
6616
6933
|
const priorMainComment = options.priorMainComment ?? await options.loadPriorMainComment?.();
|
|
6617
6934
|
const priorReviewState = priorReviewStateForSelectedTasks(loadedPriorReviewState, selectedTasks);
|
|
6935
|
+
const piRuns = [];
|
|
6618
6936
|
const runtimeOptions = {
|
|
6619
6937
|
...options,
|
|
6620
6938
|
priorReviewState,
|
|
6621
6939
|
priorMainComment,
|
|
6622
|
-
runId
|
|
6940
|
+
runId,
|
|
6941
|
+
piRunSink(run) {
|
|
6942
|
+
piRuns.push(run);
|
|
6943
|
+
}
|
|
6623
6944
|
};
|
|
6624
6945
|
const manifestCache = /* @__PURE__ */ new Map();
|
|
6625
6946
|
const taskResults = await Promise.all(tasks.map(async (task, taskOrder) => {
|
|
@@ -6703,14 +7024,19 @@ async function runTaskRuntime(options) {
|
|
|
6703
7024
|
provider,
|
|
6704
7025
|
diffManifest,
|
|
6705
7026
|
priorReviewState,
|
|
6706
|
-
runId
|
|
7027
|
+
runId,
|
|
7028
|
+
piRunSink: runtimeOptions.piRunSink
|
|
6707
7029
|
});
|
|
7030
|
+
const stats = reviewStatsForRuns(piRuns, Date.now() - runtimeStarted);
|
|
6708
7031
|
const publishing = buildCommentPublishingPlan({
|
|
6709
7032
|
event: options.event,
|
|
6710
7033
|
main,
|
|
6711
7034
|
validated,
|
|
6712
7035
|
manifest: diffManifest,
|
|
6713
7036
|
maxInlineComments: config.publication.maxInlineComments,
|
|
7037
|
+
showHeader: config.publication.showHeader,
|
|
7038
|
+
showFooter: config.publication.showFooter,
|
|
7039
|
+
showStats: config.publication.showStats,
|
|
6714
7040
|
priorReviewState: verifier.priorReviewState,
|
|
6715
7041
|
threadActions: verifier.threadActions,
|
|
6716
7042
|
metadata: {
|
|
@@ -6723,7 +7049,8 @@ async function runTaskRuntime(options) {
|
|
|
6723
7049
|
selectedTasks,
|
|
6724
7050
|
failedTasks: [],
|
|
6725
7051
|
validFindings: validated.validFindings.length,
|
|
6726
|
-
droppedFindings: validated.droppedFindings.length
|
|
7052
|
+
droppedFindings: validated.droppedFindings.length,
|
|
7053
|
+
...stats ? { stats } : {}
|
|
6727
7054
|
}
|
|
6728
7055
|
});
|
|
6729
7056
|
const publicationPlan = publishing.publicationPlan;
|
|
@@ -6782,7 +7109,8 @@ async function runSynchronizeVerifier(options) {
|
|
|
6782
7109
|
priorReviewState: options.priorReviewState,
|
|
6783
7110
|
threadContexts: await options.options.loadInlineThreadContexts?.() ?? [],
|
|
6784
7111
|
mode: { kind: "synchronize" },
|
|
6785
|
-
runId: options.runId
|
|
7112
|
+
runId: options.runId,
|
|
7113
|
+
piRunSink: options.piRunSink
|
|
6786
7114
|
});
|
|
6787
7115
|
}
|
|
6788
7116
|
function createTaskContext(options) {
|
|
@@ -6843,7 +7171,8 @@ function createTaskContext(options) {
|
|
|
6843
7171
|
runtime: {
|
|
6844
7172
|
...options,
|
|
6845
7173
|
taskContext,
|
|
6846
|
-
runId: options.runId
|
|
7174
|
+
runId: options.runId,
|
|
7175
|
+
piRunSink: options.piRunSink
|
|
6847
7176
|
}
|
|
6848
7177
|
});
|
|
6849
7178
|
options.output.providerModels.push(...result.providerModels);
|
|
@@ -6904,6 +7233,9 @@ function skippedTaskRuntimeResult(options) {
|
|
|
6904
7233
|
validated,
|
|
6905
7234
|
manifest: options.diffManifest,
|
|
6906
7235
|
maxInlineComments: options.config.publication.maxInlineComments,
|
|
7236
|
+
showHeader: options.config.publication.showHeader,
|
|
7237
|
+
showFooter: options.config.publication.showFooter,
|
|
7238
|
+
showStats: options.config.publication.showStats,
|
|
6907
7239
|
metadata: {
|
|
6908
7240
|
runtimeVersion,
|
|
6909
7241
|
configVersion: options.versionCompatibility?.configVersion,
|
|
@@ -9167,4 +9499,4 @@ async function runActionCommandWithDependencies(options) {
|
|
|
9167
9499
|
//#endregion
|
|
9168
9500
|
export { runInspectCommand as a, PublicationError as c, supportedOfficialInitRecipes as d, piBuiltinToolNames as f, piThinkingLevels as h, runInitCommand as i, supportedOfficialInitAdapters as l, piRequiredCliFlags as m, runActionCommandWithDependencies as n, runLocalReviewCommand as o, piReadOnlyToolNames as p, runDryRunCommand as r, runValidateCommand as s, runActionCommand as t, listOfficialInitRecipes as u };
|
|
9169
9501
|
|
|
9170
|
-
//# sourceMappingURL=commands-
|
|
9502
|
+
//# sourceMappingURL=commands-RY37Y0rG.mjs.map
|