diffsplain 0.10.0 → 0.11.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 +1 -0
- package/dist/assets/{angular-html-2u-u5H-N.js → angular-html-CjYwnGyV.js} +1 -1
- package/dist/assets/{angular-ts-4_HVGlKp.js → angular-ts-DSPf2G4O.js} +1 -1
- package/dist/assets/{apl-icaNWZX3.js → apl-B9ytghJx.js} +1 -1
- package/dist/assets/{astro-HvIOE4vz.js → astro-BsGV0yBF.js} +1 -1
- package/dist/assets/{blade-BpKJV8ev.js → blade-Cc2eCYlt.js} +1 -1
- package/dist/assets/{c-BNvtxyRQ.js → c-CWKyCnUx.js} +1 -1
- package/dist/assets/{cobol-8js4hfxa.js → cobol-DkamOI1E.js} +1 -1
- package/dist/assets/{coffee-DXg_piQ9.js → coffee-DyaEXePe.js} +1 -1
- package/dist/assets/{cpp-BZDFAjyh.js → cpp-16-d2Eyo.js} +1 -1
- package/dist/assets/{crystal-aCF3ira4.js → crystal-CavPPRhQ.js} +1 -1
- package/dist/assets/{css-DfRtUkhE.js → css-BME_wHJq.js} +1 -1
- package/dist/assets/{edge-vS6_AmQv.js → edge-DeVi2hvK.js} +1 -1
- package/dist/assets/{elixir-Ce0nUHSq.js → elixir-CCWgH_vc.js} +1 -1
- package/dist/assets/{elm-DmaeyizZ.js → elm-CZ570J7x.js} +1 -1
- package/dist/assets/{erb-DD6OUyvt.js → erb-CDPT5bea.js} +1 -1
- package/dist/assets/{git-rebase-HbJMn7iS.js → git-rebase-Dq5Z37DJ.js} +1 -1
- package/dist/assets/{glimmer-js-BRuqbNaV.js → glimmer-js-BoPVZK8p.js} +1 -1
- package/dist/assets/{glimmer-ts-ZBpkdEEA.js → glimmer-ts-DazEWfg1.js} +1 -1
- package/dist/assets/{glsl-CrQ4GScf.js → glsl-DCUjYCZY.js} +1 -1
- package/dist/assets/{graphql-Tn6PBdQY.js → graphql-DkCeSxy7.js} +1 -1
- package/dist/assets/{hack-CoApvrbo.js → hack-tiY3l-W-.js} +1 -1
- package/dist/assets/{haml-COKq8Xjp.js → haml-DyQZrJg_.js} +1 -1
- package/dist/assets/{handlebars-CAna2DTA.js → handlebars-Bvu5Rr0t.js} +1 -1
- package/dist/assets/{html-Ch4xJ7YF.js → html-C60VFgKW.js} +1 -1
- package/dist/assets/{html-derivative-0ITK-MAI.js → html-derivative-DB69gpnL.js} +1 -1
- package/dist/assets/{http-DiMWQ8Qr.js → http-DmS8hddd.js} +1 -1
- package/dist/assets/{hurl-OcXymC0J.js → hurl-Cdp6lmUx.js} +1 -1
- package/dist/assets/{index-BmcCug2M.js → index-CUiLG0r9.js} +4 -4
- package/dist/assets/{index-CADUNiTy.css → index-YnLIxcTm.css} +1 -1
- package/dist/assets/{java-CFb3qN4r.js → java-CkOnwoVQ.js} +1 -1
- package/dist/assets/{javascript-CRmk2JrF.js → javascript-D2tF_Ylj.js} +1 -1
- package/dist/assets/{jinja-Da6lDTEy.js → jinja-CdWpLcff.js} +1 -1
- package/dist/assets/{jison-Bnivzmtu.js → jison-BII58uuR.js} +1 -1
- package/dist/assets/{json-m8vJP_KK.js → json-BKlPRLrU.js} +1 -1
- package/dist/assets/{jsx-C3HX_Sbu.js → jsx-Ba5UouxN.js} +1 -1
- package/dist/assets/{julia-Ckg-wSHA.js → julia-CwdQF8Ff.js} +1 -1
- package/dist/assets/{just-DCjIAyvO.js → just-Be3sr1-h.js} +1 -1
- package/dist/assets/{latex-CrYSm4pl.js → latex-CDF6t93N.js} +1 -1
- package/dist/assets/{liquid-MQUFSPYU.js → liquid-BE3LVHkw.js} +1 -1
- package/dist/assets/{lua-DcuzJJ77.js → lua-D_W8Ux9S.js} +1 -1
- package/dist/assets/{marko-4JNmQaB4.js → marko-BeUfMD-U.js} +1 -1
- package/dist/assets/{mdc-C_j8HvRL.js → mdc-C8tRJlVU.js} +1 -1
- package/dist/assets/{nginx-ta3Gn1HE.js → nginx-CLPimMM6.js} +1 -1
- package/dist/assets/{nim-BwWLst9M.js → nim-ikQhXKQf.js} +1 -1
- package/dist/assets/{perl-DfW7Uq5O.js → perl-C27cToFa.js} +1 -1
- package/dist/assets/{php-DGf5G3Cf.js → php-Btdg-RP3.js} +1 -1
- package/dist/assets/{pug-CWI-NaYz.js → pug-Bk73ERXL.js} +1 -1
- package/dist/assets/{qml-Csblgh7U.js → qml-DhY7FDIl.js} +1 -1
- package/dist/assets/{r-QGooaGDI.js → r-CEwua7nj.js} +1 -1
- package/dist/assets/{razor-Dp9ihLYf.js → razor-sPO7Sh9d.js} +1 -1
- package/dist/assets/{regexp-DwUnLcVr.js → regexp-D9DLFGXE.js} +1 -1
- package/dist/assets/{rst-BY8GIwDJ.js → rst-YhauCnId.js} +1 -1
- package/dist/assets/{ruby-VgzI9htT.js → ruby-DpMkRaoz.js} +1 -1
- package/dist/assets/{sas-DYLsm-0j.js → sas-0KdmKGRO.js} +1 -1
- package/dist/assets/{scss-tIFkeUQX.js → scss-CohvHWvq.js} +1 -1
- package/dist/assets/{shellscript-CHjuQ4Pl.js → shellscript-C93Q8fLt.js} +1 -1
- package/dist/assets/{shellsession-Bv0S0hj9.js → shellsession-YCh2AQXu.js} +1 -1
- package/dist/assets/{soy-D6U1tZod.js → soy-CWVYdXUH.js} +1 -1
- package/dist/assets/{sql-CGz8kjKv.js → sql-CJNtax5I.js} +1 -1
- package/dist/assets/{stata-C9ytERUq.js → stata-C7v41XAM.js} +1 -1
- package/dist/assets/{surrealql-DRfRnaYu.js → surrealql-CRUSOxDf.js} +1 -1
- package/dist/assets/{svelte-wYnk_1sI.js → svelte-Z0harRr1.js} +1 -1
- package/dist/assets/{templ-DWrGVE0u.js → templ-BplDouJX.js} +1 -1
- package/dist/assets/{tex-CYTzoUJh.js → tex-MsB6StN5.js} +1 -1
- package/dist/assets/{ts-tags-1TIXEGea.js → ts-tags-BWx8aq5t.js} +1 -1
- package/dist/assets/{tsx-a3BoAoOh.js → tsx-DBT1gmNl.js} +1 -1
- package/dist/assets/{twig-e3dvNv6o.js → twig-jz67rR3C.js} +1 -1
- package/dist/assets/{typescript-BBhnpnTf.js → typescript-CzwGN36B.js} +1 -1
- package/dist/assets/{vue-html-quBNv38K.js → vue-html-Dhrmu7sO.js} +1 -1
- package/dist/assets/{vue-vine-CDuR69FE.js → vue-vine-BfQGXZs6.js} +1 -1
- package/dist/assets/{vue-DzzVbu9q.js → vue-xsrSiVMC.js} +1 -1
- package/dist/assets/{xml-DMifOLSe.js → xml-W7cP4qeN.js} +1 -1
- package/dist/assets/{xsl-D8Ef-5HK.js → xsl-BlXf8qm_.js} +1 -1
- package/dist/assets/{yaml-5c38gpmA.js → yaml-BtNwxUT9.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/scripts/agent-usage.mjs +141 -0
- package/scripts/build-diff-data.mjs +32 -12
- package/scripts/cli-args.mjs +7 -0
- package/scripts/coding-agents.mjs +208 -7
- package/scripts/generate-summaries.mjs +61 -9
- package/scripts/present.mjs +22 -7
- package/scripts/review-chat-context.mjs +6 -4
- package/scripts/review-chat-controller.mjs +5 -3
- package/scripts/review-chat-provider.mjs +26 -4
- package/scripts/serve-built.mjs +100 -4
|
@@ -9,6 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import {
|
|
10
10
|
agentCommand,
|
|
11
11
|
parseAgentResponse,
|
|
12
|
+
parseAgentUsage,
|
|
12
13
|
} from './coding-agents.mjs';
|
|
13
14
|
|
|
14
15
|
const maximumProviderOutputBytes = 1024 * 1024;
|
|
@@ -99,8 +100,10 @@ function providerOptions(options) {
|
|
|
99
100
|
binary: providerBinary(options, agent),
|
|
100
101
|
model: options.model,
|
|
101
102
|
reasoning: options.reasoning,
|
|
103
|
+
fast: options.fast === true,
|
|
102
104
|
accessMode: options.accessMode,
|
|
103
105
|
env: providerEnvironment(options),
|
|
106
|
+
onUsage: typeof options.onUsage === 'function' ? options.onUsage : noChange,
|
|
104
107
|
};
|
|
105
108
|
}
|
|
106
109
|
|
|
@@ -124,6 +127,7 @@ function providerInvocation(options, request, paths) {
|
|
|
124
127
|
binary: options.binary,
|
|
125
128
|
model: options.model,
|
|
126
129
|
reasoning: options.reasoning,
|
|
130
|
+
fast: options.fast,
|
|
127
131
|
prompt: request.prompt,
|
|
128
132
|
schema: request.responseSchema,
|
|
129
133
|
schemaPath: paths.schemaPath,
|
|
@@ -145,12 +149,15 @@ function preparedProviderRequest(options, request) {
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
class ChildExecution {
|
|
148
|
-
constructor({ invocation, input, agent, signal, temporary }) {
|
|
152
|
+
constructor({ invocation, input, agent, signal, temporary, onUsage, usageContext }) {
|
|
149
153
|
this.invocation = invocation;
|
|
150
154
|
this.input = input;
|
|
151
155
|
this.agent = agent;
|
|
152
156
|
this.signal = signal;
|
|
153
157
|
this.temporary = temporary;
|
|
158
|
+
this.onUsage = onUsage;
|
|
159
|
+
this.usageContext = usageContext;
|
|
160
|
+
this.usageReported = false;
|
|
154
161
|
this.child = undefined;
|
|
155
162
|
this.settled = false;
|
|
156
163
|
this.closed = false;
|
|
@@ -277,11 +284,23 @@ class ChildExecution {
|
|
|
277
284
|
finishClose() {
|
|
278
285
|
if (this.closed) return;
|
|
279
286
|
this.closed = true;
|
|
287
|
+
this.reportUsage();
|
|
280
288
|
this.clearForceKill();
|
|
281
289
|
removeTemporary(this.temporary);
|
|
282
290
|
this.resolveFinished();
|
|
283
291
|
}
|
|
284
292
|
|
|
293
|
+
reportUsage() {
|
|
294
|
+
if (this.usageReported) return;
|
|
295
|
+
this.usageReported = true;
|
|
296
|
+
try {
|
|
297
|
+
this.onUsage({
|
|
298
|
+
...this.usageContext,
|
|
299
|
+
usage: parseAgentUsage(this.agent, childOutput(this.stdout)),
|
|
300
|
+
});
|
|
301
|
+
} catch {}
|
|
302
|
+
}
|
|
303
|
+
|
|
285
304
|
resolve(value) {
|
|
286
305
|
this.settle(this.resolvePromise, value);
|
|
287
306
|
}
|
|
@@ -300,6 +319,7 @@ class ChildExecution {
|
|
|
300
319
|
|
|
301
320
|
closeChild(status, signal) {
|
|
302
321
|
this.clearForceKill();
|
|
322
|
+
this.reportUsage();
|
|
303
323
|
if (!this.settled) {
|
|
304
324
|
const error = closeError(this, status, signal);
|
|
305
325
|
if (error) this.reject(error);
|
|
@@ -325,7 +345,7 @@ function trackExecution(executions, execution) {
|
|
|
325
345
|
return execution;
|
|
326
346
|
}
|
|
327
347
|
|
|
328
|
-
function runRequest(options, executions, request) {
|
|
348
|
+
function runRequest(options, executions, request, usageContext = {}) {
|
|
329
349
|
const prepared = preparedProviderRequest(options, request);
|
|
330
350
|
const controller = new AbortController();
|
|
331
351
|
const execution = providerChild({
|
|
@@ -334,6 +354,8 @@ function runRequest(options, executions, request) {
|
|
|
334
354
|
agent: options.agent,
|
|
335
355
|
signal: controller.signal,
|
|
336
356
|
temporary: prepared.temporary,
|
|
357
|
+
onUsage: options.onUsage,
|
|
358
|
+
usageContext,
|
|
337
359
|
});
|
|
338
360
|
const tracked = trackExecution(executions, execution);
|
|
339
361
|
return {
|
|
@@ -353,8 +375,8 @@ export function createCodingAgentChatProvider(options = {}) {
|
|
|
353
375
|
const configured = providerOptions(options);
|
|
354
376
|
const executions = new Set();
|
|
355
377
|
return {
|
|
356
|
-
run(request) {
|
|
357
|
-
return runRequest(configured, executions, request);
|
|
378
|
+
run(request, context) {
|
|
379
|
+
return runRequest(configured, executions, request, context);
|
|
358
380
|
},
|
|
359
381
|
close() {
|
|
360
382
|
closeExecutions(executions);
|
package/scripts/serve-built.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
4
|
-
import { watchFile, unwatchFile } from 'node:fs';
|
|
3
|
+
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
4
|
+
import { readFileSync, watchFile, unwatchFile } from 'node:fs';
|
|
5
5
|
import { readFile, stat } from 'node:fs/promises';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { dirname, extname, resolve, sep } from 'node:path';
|
|
@@ -12,6 +12,13 @@ import {
|
|
|
12
12
|
createReviewChat,
|
|
13
13
|
} from './review-chat.mjs';
|
|
14
14
|
import { publicError } from './review-chat-context.mjs';
|
|
15
|
+
import {
|
|
16
|
+
emptyUsageAccumulator,
|
|
17
|
+
formatReviewUsage,
|
|
18
|
+
recordUsage,
|
|
19
|
+
reviewUsage,
|
|
20
|
+
usageSummary,
|
|
21
|
+
} from './agent-usage.mjs';
|
|
15
22
|
|
|
16
23
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
24
|
const clientRoot = resolve(root, 'dist');
|
|
@@ -38,6 +45,7 @@ const chatAgent = option('--chat-agent');
|
|
|
38
45
|
const chatBinary = option('--chat-binary', chatAgent);
|
|
39
46
|
const chatModel = option('--chat-model');
|
|
40
47
|
const chatReasoning = option('--chat-reasoning');
|
|
48
|
+
const chatFast = rawArgs.includes('--chat-fast');
|
|
41
49
|
const chatAccessMode = option('--chat-access-mode', 'snapshot-only');
|
|
42
50
|
const chatAccessRoot = option('--chat-access-root');
|
|
43
51
|
if (!/^\d+$/.test(portValue) || Number(portValue) > 65_535) {
|
|
@@ -266,7 +274,7 @@ function matchingAccess(value, expectedValue) {
|
|
|
266
274
|
|
|
267
275
|
async function fetchAsset(url) {
|
|
268
276
|
if (url.pathname === '/diff-data.json') {
|
|
269
|
-
return
|
|
277
|
+
return liveSnapshotResponse();
|
|
270
278
|
}
|
|
271
279
|
const pathname = decodeURIComponent(url.pathname);
|
|
272
280
|
const file = resolve(
|
|
@@ -279,6 +287,44 @@ async function fetchAsset(url) {
|
|
|
279
287
|
return fileResponse(file);
|
|
280
288
|
}
|
|
281
289
|
|
|
290
|
+
async function parsedLiveSnapshot() {
|
|
291
|
+
try {
|
|
292
|
+
return JSON.parse(await readFile(output, 'utf8'));
|
|
293
|
+
} catch {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function liveSnapshotVersion(snapshot, usage) {
|
|
299
|
+
return createHash('sha256')
|
|
300
|
+
.update(JSON.stringify({ version: snapshot.version, usage }))
|
|
301
|
+
.digest('hex')
|
|
302
|
+
.slice(0, 12);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function snapshotNoteSummary(snapshot) {
|
|
306
|
+
return snapshot?.usage?.agentNotes ||
|
|
307
|
+
usageSummary(emptyUsageAccumulator());
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function snapshotResponse(snapshot, usage) {
|
|
311
|
+
return jsonResponse({
|
|
312
|
+
...snapshot,
|
|
313
|
+
version: liveSnapshotVersion(snapshot, usage),
|
|
314
|
+
usage,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function liveSnapshotResponse() {
|
|
319
|
+
const snapshot = await parsedLiveSnapshot();
|
|
320
|
+
if (!snapshot) return fileResponse(output, { live: true });
|
|
321
|
+
const fingerprint = snapshot?.notes?.reviewFingerprint;
|
|
322
|
+
if (!fingerprint) return fileResponse(output, { live: true });
|
|
323
|
+
const chatSummary = currentChatUsage(fingerprint);
|
|
324
|
+
const usage = reviewUsage(snapshotNoteSummary(snapshot), chatSummary);
|
|
325
|
+
return snapshotResponse(snapshot, usage);
|
|
326
|
+
}
|
|
327
|
+
|
|
282
328
|
function jsonResponse(value, status = 200) {
|
|
283
329
|
return webResponse(JSON.stringify(value), {
|
|
284
330
|
status,
|
|
@@ -399,6 +445,53 @@ function logObservedError(area, error) {
|
|
|
399
445
|
console.log(`Diffsplain ${area.replaceAll('-', ' ')} error: ${publicError(error)}`);
|
|
400
446
|
}
|
|
401
447
|
|
|
448
|
+
let chatUsageFingerprint;
|
|
449
|
+
let chatUsage = emptyUsageAccumulator();
|
|
450
|
+
let reviewChat;
|
|
451
|
+
|
|
452
|
+
function snapshotUsageState() {
|
|
453
|
+
try {
|
|
454
|
+
const snapshot = JSON.parse(readFileSync(output, 'utf8'));
|
|
455
|
+
return {
|
|
456
|
+
agentNotes: snapshot.usage?.agentNotes,
|
|
457
|
+
fingerprint: snapshot.notes?.reviewFingerprint,
|
|
458
|
+
};
|
|
459
|
+
} catch {
|
|
460
|
+
return {};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function currentChatUsage(fingerprint) {
|
|
465
|
+
return fingerprint === chatUsageFingerprint
|
|
466
|
+
? usageSummary(chatUsage)
|
|
467
|
+
: usageSummary(emptyUsageAccumulator());
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function resetChatUsage(fingerprint) {
|
|
471
|
+
if (fingerprint === chatUsageFingerprint) return;
|
|
472
|
+
chatUsageFingerprint = fingerprint;
|
|
473
|
+
chatUsage = emptyUsageAccumulator();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function recordChatUsage({ reviewFingerprint, usage }) {
|
|
477
|
+
const snapshot = snapshotUsageState();
|
|
478
|
+
if (
|
|
479
|
+
!reviewFingerprint ||
|
|
480
|
+
snapshot.fingerprint !== reviewFingerprint ||
|
|
481
|
+
reviewChat?.getState().fingerprint !== reviewFingerprint
|
|
482
|
+
) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
resetChatUsage(reviewFingerprint);
|
|
486
|
+
chatUsage = recordUsage(chatUsage, usage);
|
|
487
|
+
const totals = reviewUsage(
|
|
488
|
+
snapshot.agentNotes || usageSummary(emptyUsageAccumulator()),
|
|
489
|
+
usageSummary(chatUsage),
|
|
490
|
+
);
|
|
491
|
+
console.log(formatReviewUsage(totals));
|
|
492
|
+
broadcastEvent('update');
|
|
493
|
+
}
|
|
494
|
+
|
|
402
495
|
const chatProviderAccess = chatAccessMode === 'checkout-read-only'
|
|
403
496
|
? { mode: chatAccessMode, root: resolve(chatAccessRoot) }
|
|
404
497
|
: { mode: chatAccessMode, reason: 'target-mismatch' };
|
|
@@ -408,10 +501,12 @@ const chatProvider = chatAgent
|
|
|
408
501
|
binary: chatBinary,
|
|
409
502
|
model: chatModel,
|
|
410
503
|
reasoning: chatReasoning,
|
|
504
|
+
fast: chatFast,
|
|
411
505
|
accessMode: chatProviderAccess,
|
|
506
|
+
onUsage: recordChatUsage,
|
|
412
507
|
})
|
|
413
508
|
: undefined;
|
|
414
|
-
|
|
509
|
+
reviewChat = createReviewChat({
|
|
415
510
|
snapshotPath: chatSnapshot,
|
|
416
511
|
provider: chatProvider,
|
|
417
512
|
accessMode: chatProviderAccess,
|
|
@@ -611,6 +706,7 @@ watchFile(output, { interval: 100 }, (current, previous) => {
|
|
|
611
706
|
return;
|
|
612
707
|
}
|
|
613
708
|
reviewChat.refresh();
|
|
709
|
+
resetChatUsage(reviewChat.getState().fingerprint);
|
|
614
710
|
broadcastEvent('update');
|
|
615
711
|
});
|
|
616
712
|
|