@skillstate/bench 2.2.2 → 3.0.1

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 (50) hide show
  1. package/README.md +1 -2
  2. package/dist/ab/engagement.d.ts +70 -0
  3. package/dist/ab/engagement.d.ts.map +1 -0
  4. package/dist/ab/engagement.js +102 -0
  5. package/dist/ab/engagement.js.map +1 -0
  6. package/dist/ab/index.d.ts +28 -0
  7. package/dist/ab/index.d.ts.map +1 -0
  8. package/dist/ab/index.js +20 -0
  9. package/dist/ab/index.js.map +1 -0
  10. package/dist/ab/opencode-usage.d.ts +52 -0
  11. package/dist/ab/opencode-usage.d.ts.map +1 -0
  12. package/dist/ab/opencode-usage.js +85 -0
  13. package/dist/ab/opencode-usage.js.map +1 -0
  14. package/dist/ab/record.d.ts +173 -0
  15. package/dist/ab/record.d.ts.map +1 -0
  16. package/dist/ab/record.js +98 -0
  17. package/dist/ab/record.js.map +1 -0
  18. package/dist/ab/replay.d.ts +193 -0
  19. package/dist/ab/replay.d.ts.map +1 -0
  20. package/dist/ab/replay.js +149 -0
  21. package/dist/ab/replay.js.map +1 -0
  22. package/dist/ab/report.d.ts +26 -0
  23. package/dist/ab/report.d.ts.map +1 -0
  24. package/dist/ab/report.js +98 -0
  25. package/dist/ab/report.js.map +1 -0
  26. package/dist/ab/stats-core.d.ts +84 -0
  27. package/dist/ab/stats-core.d.ts.map +1 -0
  28. package/dist/ab/stats-core.js +93 -0
  29. package/dist/ab/stats-core.js.map +1 -0
  30. package/dist/ab/survey.d.ts +131 -0
  31. package/dist/ab/survey.d.ts.map +1 -0
  32. package/dist/ab/survey.js +143 -0
  33. package/dist/ab/survey.js.map +1 -0
  34. package/dist/ab/usage.d.ts +99 -0
  35. package/dist/ab/usage.d.ts.map +1 -0
  36. package/dist/ab/usage.js +98 -0
  37. package/dist/ab/usage.js.map +1 -0
  38. package/dist/ab/verdict.d.ts +88 -0
  39. package/dist/ab/verdict.d.ts.map +1 -0
  40. package/dist/ab/verdict.js +251 -0
  41. package/dist/ab/verdict.js.map +1 -0
  42. package/dist/ab-cli.d.ts +35 -0
  43. package/dist/ab-cli.d.ts.map +1 -0
  44. package/dist/ab-cli.js +161 -0
  45. package/dist/ab-cli.js.map +1 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -0
  49. package/dist/index.js.map +1 -1
  50. package/package.json +6 -2
package/README.md CHANGED
@@ -6,8 +6,7 @@
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@skillstate/bench)](https://www.npmjs.com/package/@skillstate/bench)
8
8
  [![node](https://img.shields.io/node/v/@skillstate/bench)](https://www.npmjs.com/package/@skillstate/bench)
9
- [![Tests](https://img.shields.io/badge/tests-873%20passing-brightgreen)](https://github.com/vitalykuzyaev/skillstate)
10
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/vitalykuzyaev/skillstate/blob/main/LICENSE)
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/vitkuz573/skillstate/blob/main/LICENSE)
11
10
 
12
11
  </div>
13
12
 
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Proving the integration was actually engaged.
3
+ *
4
+ * ── The single most important check in the harness ────────────────────────
5
+ *
6
+ * The previous experiment produced a confident "39% saving" from a run in
7
+ * which the instrumented arm never wrote the state file once. The model was
8
+ * never asked to use the tools, or asked and declined; either way the plugin
9
+ * was decoration, and the token delta was a sample of how the model varies
10
+ * between two identical runs.
11
+ *
12
+ * Nothing in the token stream can distinguish those cases. Both arms produce
13
+ * perfectly ordinary messages. The only evidence that a difference was caused
14
+ * by the integration is that the integration left a mark — and the mark is
15
+ * the state file.
16
+ *
17
+ * So engagement is measured by DIFFING the state file, not by asking the
18
+ * model. A model that reports having used the tools is not evidence; bytes
19
+ * on disk are.
20
+ *
21
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
22
+ */
23
+ import type { EngagementEvidence } from './record.js';
24
+ /** One observed transition of the state file. */
25
+ export interface StateSample {
26
+ /** Which trial the sample belongs to. */
27
+ readonly trial: number;
28
+ /**
29
+ * Point in the run at which the file was read: 0 before the first turn,
30
+ * n after the last.
31
+ */
32
+ readonly step: number;
33
+ /** Raw file content, or `null` when no file existed. */
34
+ readonly content: string | null;
35
+ /** True when a `state_patch` from the response sink caused this sample. */
36
+ readonly fromSink?: boolean;
37
+ }
38
+ /** The result of watching a run's state file. */
39
+ export interface EngagementReport {
40
+ readonly evidence: EngagementEvidence;
41
+ /** Samples whose content differed from the previous one, in order. */
42
+ readonly transitions: readonly StateSample[];
43
+ }
44
+ /**
45
+ * Reduce a run's state samples to engagement evidence.
46
+ *
47
+ * A "write" is a change in content, not a call to the write function. A sink
48
+ * that re-writes the same bytes has changed nothing, and counting it would
49
+ * let a retry loop manufacture the appearance of engagement — the mirror
50
+ * image of the bug this module exists to catch.
51
+ *
52
+ * The first sample is the baseline, so a file that already existed at step 0
53
+ * is not itself evidence of a write. That distinction matters for a resumed
54
+ * session, where `hadStateAtStep0` is true and a naive diff would report
55
+ * engagement even if the model never touched anything.
56
+ */
57
+ export declare function assessEngagement(samples: readonly StateSample[]): EngagementReport;
58
+ /** Attach sink rejection counts to evidence produced by {@link assessEngagement}. */
59
+ export declare function withRejections(evidence: EngagementEvidence, rejections: number, firstRejection?: string): EngagementEvidence;
60
+ /**
61
+ * Whether a set of state samples is sufficient to judge engagement.
62
+ *
63
+ * One sample cannot: it is equally consistent with "the file never changed"
64
+ * and with "the file changed and was not sampled". The harness treats an
65
+ * under-sampled run as unmeasured rather than as inert, because reporting
66
+ * "inert" for a run nobody watched would be a false accusation of the
67
+ * integration, which is the one error this harness must never make.
68
+ */
69
+ export declare function isWitnessed(samples: readonly StateSample[]): boolean;
70
+ //# sourceMappingURL=engagement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engagement.d.ts","sourceRoot":"","sources":["../../src/ab/engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,SAAS,WAAW,EAAE,CAAC;CAC9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,gBAAgB,CAqClF;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,kBAAkB,EAC5B,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,kBAAkB,CAMpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAWpE"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Proving the integration was actually engaged.
3
+ *
4
+ * ── The single most important check in the harness ────────────────────────
5
+ *
6
+ * The previous experiment produced a confident "39% saving" from a run in
7
+ * which the instrumented arm never wrote the state file once. The model was
8
+ * never asked to use the tools, or asked and declined; either way the plugin
9
+ * was decoration, and the token delta was a sample of how the model varies
10
+ * between two identical runs.
11
+ *
12
+ * Nothing in the token stream can distinguish those cases. Both arms produce
13
+ * perfectly ordinary messages. The only evidence that a difference was caused
14
+ * by the integration is that the integration left a mark — and the mark is
15
+ * the state file.
16
+ *
17
+ * So engagement is measured by DIFFING the state file, not by asking the
18
+ * model. A model that reports having used the tools is not evidence; bytes
19
+ * on disk are.
20
+ *
21
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
22
+ */
23
+ /**
24
+ * Reduce a run's state samples to engagement evidence.
25
+ *
26
+ * A "write" is a change in content, not a call to the write function. A sink
27
+ * that re-writes the same bytes has changed nothing, and counting it would
28
+ * let a retry loop manufacture the appearance of engagement — the mirror
29
+ * image of the bug this module exists to catch.
30
+ *
31
+ * The first sample is the baseline, so a file that already existed at step 0
32
+ * is not itself evidence of a write. That distinction matters for a resumed
33
+ * session, where `hadStateAtStep0` is true and a naive diff would report
34
+ * engagement even if the model never touched anything.
35
+ */
36
+ export function assessEngagement(samples) {
37
+ const ordered = [...samples].sort((a, b) => a.trial - b.trial || a.step - b.step);
38
+ const transitions = [];
39
+ let previous = null;
40
+ let writes = 0;
41
+ let sinkWrites = 0;
42
+ // Tracked separately from `previous` because `previous === null` is a
43
+ // legitimate first value (no state file yet) and cannot double as the
44
+ // "is this the baseline sample" marker.
45
+ let isBaseline = true;
46
+ for (const sample of ordered) {
47
+ if (isBaseline) {
48
+ isBaseline = false;
49
+ previous = sample.content;
50
+ continue;
51
+ }
52
+ if (sample.content === previous)
53
+ continue;
54
+ transitions.push(sample);
55
+ writes += 1;
56
+ if (sample.fromSink === true)
57
+ sinkWrites += 1;
58
+ previous = sample.content;
59
+ }
60
+ const first = ordered[0];
61
+ return {
62
+ evidence: {
63
+ engaged: writes > 0,
64
+ writes,
65
+ sinkWrites,
66
+ rejections: 0,
67
+ hadStateAtStep0: (first?.content ?? null) !== null,
68
+ },
69
+ transitions,
70
+ };
71
+ }
72
+ /** Attach sink rejection counts to evidence produced by {@link assessEngagement}. */
73
+ export function withRejections(evidence, rejections, firstRejection) {
74
+ return {
75
+ ...evidence,
76
+ rejections,
77
+ ...(firstRejection === undefined ? {} : { firstRejection }),
78
+ };
79
+ }
80
+ /**
81
+ * Whether a set of state samples is sufficient to judge engagement.
82
+ *
83
+ * One sample cannot: it is equally consistent with "the file never changed"
84
+ * and with "the file changed and was not sampled". The harness treats an
85
+ * under-sampled run as unmeasured rather than as inert, because reporting
86
+ * "inert" for a run nobody watched would be a false accusation of the
87
+ * integration, which is the one error this harness must never make.
88
+ */
89
+ export function isWitnessed(samples) {
90
+ if (samples.length === 0)
91
+ return false;
92
+ const byTrial = new Map();
93
+ for (const sample of samples) {
94
+ const steps = byTrial.get(sample.trial) ?? [];
95
+ steps.push(sample.step);
96
+ byTrial.set(sample.trial, steps);
97
+ }
98
+ // Every trial needs at least two samples — a before and an after — or a
99
+ // change between them is unobservable.
100
+ return [...byTrial.values()].every((steps) => steps.length >= 2);
101
+ }
102
+ //# sourceMappingURL=engagement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engagement.js","sourceRoot":"","sources":["../../src/ab/engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA0BH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA+B;IAC9D,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAC/C,CAAC;IACF,MAAM,WAAW,GAAkB,EAAE,CAAC;IACtC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,sEAAsE;IACtE,sEAAsE;IACtE,wCAAwC;IACxC,IAAI,UAAU,GAAG,IAAI,CAAC;IAEtB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,GAAG,KAAK,CAAC;YACnB,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ;YAAE,SAAS;QAC1C,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,IAAI,CAAC,CAAC;QACZ,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI;YAAE,UAAU,IAAI,CAAC,CAAC;QAC9C,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;IAC5B,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO;QACL,QAAQ,EAAE;YACR,OAAO,EAAE,MAAM,GAAG,CAAC;YACnB,MAAM;YACN,UAAU;YACV,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI;SACnD;QACD,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAC5B,QAA4B,EAC5B,UAAkB,EAClB,cAAuB;IAEvB,OAAO;QACL,GAAG,QAAQ;QACX,UAAU;QACV,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAA+B;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IACD,wEAAwE;IACxE,uCAAuC;IACvC,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The A/B harness: a run record per trial, and gates on the result.
3
+ *
4
+ * Everything here is either pure or injected. The live driver
5
+ * (`run-live.ts`) supplies the I/O; the gates in `verdict.ts` and the
6
+ * statistics in `stats-core.ts` never touch a filesystem or a clock, which
7
+ * is what allows them to be tested against the real historical numbers.
8
+ *
9
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
10
+ */
11
+ export { ARM_IDS, CONTROL_ENGAGEMENT, buildArmRecord, isInstrumented, promptTokens, } from './record.js';
12
+ export type { ArmId, ArmRecord, EngagementEvidence, RunRecord, TokenUsage, WorkEvidence, } from './record.js';
13
+ export { MIN_EFFECT_IN_MADS, describe, effectSize, mad, median, relativeMad, } from './stats-core.js';
14
+ export type { Distribution, EffectSize } from './stats-core.js';
15
+ export { MIN_TRIALS, runExperiment } from './verdict.js';
16
+ export type { ExperimentOptions, GateFailure, Verdict, VerdictResult, } from './verdict.js';
17
+ export { assessEngagement, isWitnessed, withRejections } from './engagement.js';
18
+ export type { EngagementReport, StateSample } from './engagement.js';
19
+ export { NO_USAGE, resolveSessionUsage, sessionUsage } from './usage.js';
20
+ export type { HostMessageRow, UsageFailure, UsageOutcome, UsageReader, } from './usage.js';
21
+ export { formatArmTable, formatVerdict } from './report.js';
22
+ export { serverSessionUsage, serverUsageReader } from './opencode-usage.js';
23
+ export type { ServerFetcher } from './opencode-usage.js';
24
+ export { DEFAULT_BOUNDED_PROMPT_TOKENS, DEFAULT_CACHE_READ_DISCOUNT, assessReconstruction, promptTokensOf, reconstruct, } from './replay.js';
25
+ export type { HostSession, ReconstructOptions, ReconstructResult, StepUsage, } from './replay.js';
26
+ export { breakEvenPromptTokens, formatSurvey, survey } from './survey.js';
27
+ export type { Survey, SurveyOptions } from './survey.js';
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ab/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,GAAG,EACH,MAAM,EACN,WAAW,GACZ,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAChF,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACzE,YAAY,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,oBAAoB,EACpB,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The A/B harness: a run record per trial, and gates on the result.
3
+ *
4
+ * Everything here is either pure or injected. The live driver
5
+ * (`run-live.ts`) supplies the I/O; the gates in `verdict.ts` and the
6
+ * statistics in `stats-core.ts` never touch a filesystem or a clock, which
7
+ * is what allows them to be tested against the real historical numbers.
8
+ *
9
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
10
+ */
11
+ export { ARM_IDS, CONTROL_ENGAGEMENT, buildArmRecord, isInstrumented, promptTokens, } from './record.js';
12
+ export { MIN_EFFECT_IN_MADS, describe, effectSize, mad, median, relativeMad, } from './stats-core.js';
13
+ export { MIN_TRIALS, runExperiment } from './verdict.js';
14
+ export { assessEngagement, isWitnessed, withRejections } from './engagement.js';
15
+ export { NO_USAGE, resolveSessionUsage, sessionUsage } from './usage.js';
16
+ export { formatArmTable, formatVerdict } from './report.js';
17
+ export { serverSessionUsage, serverUsageReader } from './opencode-usage.js';
18
+ export { DEFAULT_BOUNDED_PROMPT_TOKENS, DEFAULT_CACHE_READ_DISCOUNT, assessReconstruction, promptTokensOf, reconstruct, } from './replay.js';
19
+ export { breakEvenPromptTokens, formatSurvey, survey } from './survey.js';
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ab/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,aAAa,CAAC;AAUrB,OAAO,EACL,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,GAAG,EACH,MAAM,EACN,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQzD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGhF,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQzE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,oBAAoB,EACpB,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Reading token usage out of an OpenCode server's own store.
3
+ *
4
+ * ── Why read the host's store instead of parsing its output ──────────────
5
+ *
6
+ * The obvious way to measure a host integration is to run the host and read
7
+ * what it prints. That fails in three ways that matter here:
8
+ *
9
+ * 1. **Quoting the measurement to the measured.** The numbers come from the
10
+ * run that is being measured, through a reporting path the run itself
11
+ * controls. The previous A/B took its figures exactly that way, and one of
12
+ * its "arms" turned out to be two readings of a single session.
13
+ * 2. **Unreliability under the conditions that matter.** A run that ends on an
14
+ * error prints its error, not its totals — which is exactly the run whose
15
+ * cost most needs checking.
16
+ * 3. **Irreproducibility.** Once a run's output is gone, its cost cannot be
17
+ * re-derived. A store keyed by session id can be re-read later, which means
18
+ * a disputed measurement can be settled without re-running anything.
19
+ *
20
+ * The store is OpenCode's own accounting, so it is not our reconstruction
21
+ * either. That makes it a third-party number rather than a self-report, which
22
+ * is a meaningfully better position than either alternative.
23
+ *
24
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
25
+ */
26
+ import { NO_USAGE } from './usage.js';
27
+ import type { HostMessageRow, UsageOutcome, UsageReader } from './usage.js';
28
+ /** The HTTP seam onto a running OpenCode server. */
29
+ export interface ServerFetcher {
30
+ /**
31
+ * `GET <baseUrl><path>`, resolving the parsed JSON body.
32
+ *
33
+ * Rejects on a non-2xx response so a 404 cannot be mistaken for a session
34
+ * with no messages — the first is a mistake in the request, the second is a
35
+ * fact about the run, and they need different fixes.
36
+ */
37
+ get(path: string): Promise<unknown>;
38
+ }
39
+ /**
40
+ * Build a {@link UsageReader} over an OpenCode server.
41
+ *
42
+ * `baseUrl` is the server root, e.g. `http://localhost:4096`. Messages are
43
+ * requested per session so the reader never has to hold the whole store in
44
+ * memory, and a session with thousands of messages costs one request rather
45
+ * than a full scan.
46
+ */
47
+ export declare function serverUsageReader(baseUrl: string, fetchJson: ServerFetcher['get']): UsageReader;
48
+ /** Resolve a session's token spend over HTTP. */
49
+ export declare function serverSessionUsage(baseUrl: string, fetchJson: ServerFetcher['get'], sessionID: string): Promise<UsageOutcome>;
50
+ export { NO_USAGE };
51
+ export type { HostMessageRow, UsageOutcome, UsageReader };
52
+ //# sourceMappingURL=opencode-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-usage.d.ts","sourceRoot":"","sources":["../../src/ab/opencode-usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,QAAQ,EAAuB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,WAAW,EACZ,MAAM,YAAY,CAAC;AA6CpB,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AAMD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC,GAC9B,WAAW,CAqBb;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Reading token usage out of an OpenCode server's own store.
3
+ *
4
+ * ── Why read the host's store instead of parsing its output ──────────────
5
+ *
6
+ * The obvious way to measure a host integration is to run the host and read
7
+ * what it prints. That fails in three ways that matter here:
8
+ *
9
+ * 1. **Quoting the measurement to the measured.** The numbers come from the
10
+ * run that is being measured, through a reporting path the run itself
11
+ * controls. The previous A/B took its figures exactly that way, and one of
12
+ * its "arms" turned out to be two readings of a single session.
13
+ * 2. **Unreliability under the conditions that matter.** A run that ends on an
14
+ * error prints its error, not its totals — which is exactly the run whose
15
+ * cost most needs checking.
16
+ * 3. **Irreproducibility.** Once a run's output is gone, its cost cannot be
17
+ * re-derived. A store keyed by session id can be re-read later, which means
18
+ * a disputed measurement can be settled without re-running anything.
19
+ *
20
+ * The store is OpenCode's own accounting, so it is not our reconstruction
21
+ * either. That makes it a third-party number rather than a self-report, which
22
+ * is a meaningfully better position than either alternative.
23
+ *
24
+ * @non-paper — measurement infrastructure, not the paper's evaluation.
25
+ */
26
+ import { NO_USAGE, resolveSessionUsage } from './usage.js';
27
+ function asNumber(value) {
28
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
29
+ }
30
+ /**
31
+ * Convert one `message.data` blob into a row.
32
+ *
33
+ * `reasoning` is folded into `output` deliberately: reasoning tokens are
34
+ * generated tokens, they are billed as such, and excluding them would make a
35
+ * thinking-heavy run look cheaper than it was.
36
+ */
37
+ function toRow(sessionID, created, data) {
38
+ const record = (typeof data === 'object' && data !== null ? data : {});
39
+ const tokens = record.tokens;
40
+ if (tokens === undefined) {
41
+ return { sessionID, created, role: 'unknown', tokens: null };
42
+ }
43
+ const usage = {
44
+ input: asNumber(tokens.input),
45
+ cacheRead: asNumber(tokens.cache?.read),
46
+ cacheWrite: asNumber(tokens.cache?.write),
47
+ output: asNumber(tokens.output) + asNumber(tokens.reasoning),
48
+ };
49
+ return {
50
+ sessionID,
51
+ created,
52
+ role: typeof record.role === 'string' ? record.role : 'unknown',
53
+ tokens: usage,
54
+ };
55
+ }
56
+ function asArray(value) {
57
+ return Array.isArray(value) ? value : [];
58
+ }
59
+ /**
60
+ * Build a {@link UsageReader} over an OpenCode server.
61
+ *
62
+ * `baseUrl` is the server root, e.g. `http://localhost:4096`. Messages are
63
+ * requested per session so the reader never has to hold the whole store in
64
+ * memory, and a session with thousands of messages costs one request rather
65
+ * than a full scan.
66
+ */
67
+ export function serverUsageReader(baseUrl, fetchJson) {
68
+ const root = baseUrl.replace(/\/+$/, '');
69
+ return {
70
+ async messagesFor(sessionID) {
71
+ const body = await fetchJson(`${root}/api/session/${encodeURIComponent(sessionID)}/message`);
72
+ return asArray(body).map((entry) => {
73
+ const record = (typeof entry === 'object' && entry !== null ? entry : {});
74
+ const info = (typeof record.info === 'object' && record.info !== null ? record.info : {});
75
+ return toRow(typeof info.sessionID === 'string' ? info.sessionID : sessionID, asNumber(info.time?.created ?? record.created), record.info);
76
+ });
77
+ },
78
+ };
79
+ }
80
+ /** Resolve a session's token spend over HTTP. */
81
+ export function serverSessionUsage(baseUrl, fetchJson, sessionID) {
82
+ return resolveSessionUsage(serverUsageReader(baseUrl, fetchJson), sessionID);
83
+ }
84
+ export { NO_USAGE };
85
+ //# sourceMappingURL=opencode-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-usage.js","sourceRoot":"","sources":["../../src/ab/opencode-usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * The shape of one A/B run record.
3
+ *
4
+ * ── Why a record and not a number ────────────────────────────────────────
5
+ *
6
+ * The previous A/B produced a number — "39% saved" — from a run in which
7
+ * the instrumented arm never once touched the state file. The number was
8
+ * arithmetically correct and scientifically meaningless, and nothing in the
9
+ * pipeline could tell the difference, because the pipeline carried a number
10
+ * instead of a record.
11
+ *
12
+ * A record keeps the things that decide whether a comparison means anything:
13
+ * what the task was, what each arm spent, and — the part that was missing —
14
+ * **whether the instrumentation was engaged at all**. Gates in `verdict.ts`
15
+ * read these fields; none of them is derivable from token counts.
16
+ *
17
+ * @non-paper — measurement infrastructure for OUR host integration. Not
18
+ * part of the paper's own evaluation.
19
+ */
20
+ /** Which integration an arm ran under. */
21
+ export type ArmId = 'plain' | 'notes' | 'paper';
22
+ /** Every arm, in the order an experiment should run them. */
23
+ export declare const ARM_IDS: readonly ArmId[];
24
+ /**
25
+ * Whether an arm has the skillstate integration switched ON.
26
+ *
27
+ * `plain` is the control: no plugin, no state, no system fragment. The other
28
+ * two carry the integration in its two modes.
29
+ */
30
+ export declare function isInstrumented(arm: ArmId): boolean;
31
+ /** Token spend, split by billing class. */
32
+ export interface TokenUsage {
33
+ /** Fresh, uncached input tokens. */
34
+ readonly input: number;
35
+ /** Input tokens served from the prompt cache. */
36
+ readonly cacheRead: number;
37
+ /** Cache writes caused by this run. */
38
+ readonly cacheWrite: number;
39
+ /** Generated tokens, including reasoning. */
40
+ readonly output: number;
41
+ }
42
+ /**
43
+ * Tokens that constitute PROMPT cost.
44
+ *
45
+ * `input + cacheRead` is what the model had to be shown, whether it was
46
+ * billed as fresh or served from cache. `cacheWrite` is excluded because it
47
+ * is an artefact of a cold cache rather than of prompt size, and folding it
48
+ * in would make a run that happened to miss the cache look like an expensive
49
+ * one. `output` is excluded because the paper's claim is about prompt
50
+ * economy, not generation economy.
51
+ */
52
+ export declare function promptTokens(usage: TokenUsage): number;
53
+ /**
54
+ * How the state file behaved during one run.
55
+ *
56
+ * This is the field whose absence made the old experiment unreadable. A run
57
+ * where `engaged` is false measured a plugin that did nothing, and its token
58
+ * count is a sample of model variance, not of the integration.
59
+ */
60
+ export interface EngagementEvidence {
61
+ /**
62
+ * Did the state file change at any point during the run?
63
+ *
64
+ * Computed by comparing the file's bytes before the first turn and after
65
+ * the last, NOT by asking the model whether it called a tool. A model
66
+ * reporting "I used the state" is not evidence; a changed file is.
67
+ */
68
+ readonly engaged: boolean;
69
+ /** How many times the state file's content changed during the run. */
70
+ readonly writes: number;
71
+ /** How many of those writes came from the paper-mode response sink. */
72
+ readonly sinkWrites: number;
73
+ /** How many times a response was rejected and never reached the state. */
74
+ readonly rejections: number;
75
+ /** First rejection reason, for diagnostics. Absent when none occurred. */
76
+ readonly firstRejection?: string;
77
+ /** True when the arm found a state file already present at step 0. */
78
+ readonly hadStateAtStep0: boolean;
79
+ }
80
+ /** Engagement for a run whose arm carries no integration. */
81
+ export declare const CONTROL_ENGAGEMENT: EngagementEvidence;
82
+ /**
83
+ * What an arm actually produced, used to decide whether the two arms did
84
+ * comparable work.
85
+ */
86
+ export interface WorkEvidence {
87
+ /**
88
+ * Content hash of the task's declared artifact, e.g. the `AUDIT.md` the
89
+ * task asks for. Equal hashes across arms is the strongest available
90
+ * check that both arms finished the same thing.
91
+ */
92
+ readonly artifactDigest: string | null;
93
+ /** Number of model turns the run took. */
94
+ readonly turns: number;
95
+ /** Tool calls made, across all tools. */
96
+ readonly toolCalls: number;
97
+ }
98
+ /** One arm of one trial, executed once. */
99
+ export interface RunRecord {
100
+ /** Which arm this was. */
101
+ readonly arm: ArmId;
102
+ /** Trial index, 0-based. Multiple trials per arm are what make variance visible. */
103
+ readonly trial: number;
104
+ /** The task both arms were given. */
105
+ readonly task: string;
106
+ /** Provider and model id, e.g. `openai/gpt-5`. Equal across arms or the run is void. */
107
+ readonly model: string;
108
+ /** Host version, e.g. `2.0.19`. Equal across arms or the run is void. */
109
+ readonly hostVersion: string;
110
+ /** Host session id, so the run can be re-read from the host's own store. */
111
+ readonly sessionID: string;
112
+ /** Token spend for the run. */
113
+ readonly usage: TokenUsage;
114
+ /** What the state file did during the run. */
115
+ readonly engagement: EngagementEvidence;
116
+ /** What the run produced. */
117
+ readonly work: WorkEvidence;
118
+ /** Wall-clock milliseconds for the run. */
119
+ readonly durationMs: number;
120
+ /** True when the run completed without a provider or host error. */
121
+ readonly completed: boolean;
122
+ /**
123
+ * Whether the run produced the correct answer, when the experiment recorded
124
+ * it.
125
+ *
126
+ * Optional because a trial file written before this field existed has none,
127
+ * and because a task with no checkable answer has none to give. What is NOT
128
+ * optional is its absence counting as a pass — hence the outcome gate, which
129
+ * refuses an experiment that measured cost and not the work.
130
+ */
131
+ readonly outcome?: Outcome;
132
+ /** Failure reason when `completed` is false. */
133
+ readonly error?: string;
134
+ }
135
+ /**
136
+ * What the run was supposed to produce, and whether it did.
137
+ *
138
+ * Separate from `completed`: a run can complete perfectly and still be wrong.
139
+ * That distinction is the whole reason this type exists — "it finished" and
140
+ * "it did the work" are different claims, and conflating them is how an
141
+ * experiment reports a saving for a run that answered nothing.
142
+ */
143
+ export interface Outcome {
144
+ readonly correct: boolean;
145
+ /** What the run reported, for a human reading the record. */
146
+ readonly reported?: string;
147
+ }
148
+ /** One arm's results across every trial it ran. */
149
+ export interface ArmRecord {
150
+ readonly arm: ArmId;
151
+ readonly task: string;
152
+ readonly model: string;
153
+ readonly hostVersion: string;
154
+ readonly runs: readonly RunRecord[];
155
+ }
156
+ /**
157
+ * Build an {@link ArmRecord} from its runs, checking they are commensurable.
158
+ *
159
+ * A trial index must be unique within an arm: two runs labelled `trial: 0`
160
+ * are two samples of the same cell, and silently averaging them would hide
161
+ * exactly the variance the harness exists to surface.
162
+ *
163
+ * Returns a typed failure rather than throwing — a malformed arm is a
164
+ * harness bug, and the caller must be able to report it as one.
165
+ */
166
+ export declare function buildArmRecord(arm: ArmId, runs: readonly RunRecord[]): {
167
+ ok: true;
168
+ record: ArmRecord;
169
+ } | {
170
+ ok: false;
171
+ reason: string;
172
+ };
173
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/ab/record.ts"],"names":[],"mappings":"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"}