@voxslopuli/openagentsy 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +21 -2
  3. package/dist/adapter-dependency.test.d.ts +2 -0
  4. package/dist/adapter-dependency.test.d.ts.map +1 -0
  5. package/dist/adapter-dependency.test.js +51 -0
  6. package/dist/adapter-dependency.test.js.map +1 -0
  7. package/dist/commands/approve.d.ts +63 -0
  8. package/dist/commands/approve.d.ts.map +1 -0
  9. package/dist/commands/approve.js +218 -0
  10. package/dist/commands/approve.js.map +1 -0
  11. package/dist/commands/approve.test.d.ts +2 -0
  12. package/dist/commands/approve.test.d.ts.map +1 -0
  13. package/dist/commands/approve.test.js +219 -0
  14. package/dist/commands/approve.test.js.map +1 -0
  15. package/dist/commands/audit.d.ts +74 -0
  16. package/dist/commands/audit.d.ts.map +1 -0
  17. package/dist/commands/audit.js +230 -0
  18. package/dist/commands/audit.js.map +1 -0
  19. package/dist/commands/audit.test.d.ts +2 -0
  20. package/dist/commands/audit.test.d.ts.map +1 -0
  21. package/dist/commands/audit.test.js +194 -0
  22. package/dist/commands/audit.test.js.map +1 -0
  23. package/dist/commands/board.d.ts +33 -0
  24. package/dist/commands/board.d.ts.map +1 -0
  25. package/dist/commands/board.js +65 -0
  26. package/dist/commands/board.js.map +1 -0
  27. package/dist/commands/build.d.ts +17 -0
  28. package/dist/commands/build.d.ts.map +1 -0
  29. package/dist/commands/build.js +145 -0
  30. package/dist/commands/build.js.map +1 -0
  31. package/dist/commands/conformance.d.ts +31 -0
  32. package/dist/commands/conformance.d.ts.map +1 -0
  33. package/dist/commands/conformance.js +57 -0
  34. package/dist/commands/conformance.js.map +1 -0
  35. package/dist/commands/conformance.test.d.ts +2 -0
  36. package/dist/commands/conformance.test.d.ts.map +1 -0
  37. package/dist/commands/conformance.test.js +74 -0
  38. package/dist/commands/conformance.test.js.map +1 -0
  39. package/dist/commands/doctor.d.ts +132 -0
  40. package/dist/commands/doctor.d.ts.map +1 -0
  41. package/dist/commands/doctor.js +768 -0
  42. package/dist/commands/doctor.js.map +1 -0
  43. package/dist/commands/doctor.test.d.ts +2 -0
  44. package/dist/commands/doctor.test.d.ts.map +1 -0
  45. package/dist/commands/doctor.test.js +329 -0
  46. package/dist/commands/doctor.test.js.map +1 -0
  47. package/dist/commands/ledger.d.ts +109 -0
  48. package/dist/commands/ledger.d.ts.map +1 -0
  49. package/dist/commands/ledger.js +286 -0
  50. package/dist/commands/ledger.js.map +1 -0
  51. package/dist/commands/ledger.test.d.ts +2 -0
  52. package/dist/commands/ledger.test.d.ts.map +1 -0
  53. package/dist/commands/ledger.test.js +238 -0
  54. package/dist/commands/ledger.test.js.map +1 -0
  55. package/dist/commands/lessons.d.ts +73 -0
  56. package/dist/commands/lessons.d.ts.map +1 -0
  57. package/dist/commands/lessons.js +97 -0
  58. package/dist/commands/lessons.js.map +1 -0
  59. package/dist/commands/lessons.test.d.ts +2 -0
  60. package/dist/commands/lessons.test.d.ts.map +1 -0
  61. package/dist/commands/lessons.test.js +146 -0
  62. package/dist/commands/lessons.test.js.map +1 -0
  63. package/dist/commands/models.d.ts +49 -0
  64. package/dist/commands/models.d.ts.map +1 -0
  65. package/dist/commands/models.js +125 -0
  66. package/dist/commands/models.js.map +1 -0
  67. package/dist/commands/report.d.ts +57 -0
  68. package/dist/commands/report.d.ts.map +1 -0
  69. package/dist/commands/report.js +164 -0
  70. package/dist/commands/report.js.map +1 -0
  71. package/dist/commands/report.test.d.ts +2 -0
  72. package/dist/commands/report.test.d.ts.map +1 -0
  73. package/dist/commands/report.test.js +294 -0
  74. package/dist/commands/report.test.js.map +1 -0
  75. package/dist/commands/scope.d.ts +34 -0
  76. package/dist/commands/scope.d.ts.map +1 -0
  77. package/dist/commands/scope.js +78 -0
  78. package/dist/commands/scope.js.map +1 -0
  79. package/dist/commands/scope.test.d.ts +2 -0
  80. package/dist/commands/scope.test.d.ts.map +1 -0
  81. package/dist/commands/scope.test.js +98 -0
  82. package/dist/commands/scope.test.js.map +1 -0
  83. package/dist/commands/setup.d.ts +31 -0
  84. package/dist/commands/setup.d.ts.map +1 -0
  85. package/dist/commands/setup.js +93 -0
  86. package/dist/commands/setup.js.map +1 -0
  87. package/dist/commands/waivers.d.ts +47 -0
  88. package/dist/commands/waivers.d.ts.map +1 -0
  89. package/dist/commands/waivers.js +68 -0
  90. package/dist/commands/waivers.js.map +1 -0
  91. package/dist/commands/waivers.test.d.ts +2 -0
  92. package/dist/commands/waivers.test.d.ts.map +1 -0
  93. package/dist/commands/waivers.test.js +64 -0
  94. package/dist/commands/waivers.test.js.map +1 -0
  95. package/dist/generate/agent.d.ts +19 -0
  96. package/dist/generate/agent.d.ts.map +1 -0
  97. package/dist/generate/agent.js +35 -0
  98. package/dist/generate/agent.js.map +1 -0
  99. package/dist/generate/agent.test.d.ts +2 -0
  100. package/dist/generate/agent.test.d.ts.map +1 -0
  101. package/dist/generate/agent.test.js +245 -0
  102. package/dist/generate/agent.test.js.map +1 -0
  103. package/dist/generate/commands.d.ts +15 -0
  104. package/dist/generate/commands.d.ts.map +1 -0
  105. package/dist/generate/commands.js +59 -0
  106. package/dist/generate/commands.js.map +1 -0
  107. package/dist/generate/lock.d.ts +18 -0
  108. package/dist/generate/lock.d.ts.map +1 -0
  109. package/dist/generate/lock.js +26 -0
  110. package/dist/generate/lock.js.map +1 -0
  111. package/dist/generate/prompt.d.ts +11 -0
  112. package/dist/generate/prompt.d.ts.map +1 -0
  113. package/dist/generate/prompt.js +114 -0
  114. package/dist/generate/prompt.js.map +1 -0
  115. package/dist/generate/prompt.test.d.ts +2 -0
  116. package/dist/generate/prompt.test.d.ts.map +1 -0
  117. package/dist/generate/prompt.test.js +59 -0
  118. package/dist/generate/prompt.test.js.map +1 -0
  119. package/dist/index.d.ts +40 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +448 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/index.test.d.ts +2 -0
  124. package/dist/index.test.d.ts.map +1 -0
  125. package/dist/index.test.js +237 -0
  126. package/dist/index.test.js.map +1 -0
  127. package/dist/opencode.d.ts +29 -0
  128. package/dist/opencode.d.ts.map +1 -0
  129. package/dist/opencode.js +125 -0
  130. package/dist/opencode.js.map +1 -0
  131. package/dist/plugin-bundle.test.d.ts +2 -0
  132. package/dist/plugin-bundle.test.d.ts.map +1 -0
  133. package/dist/plugin-bundle.test.js +43 -0
  134. package/dist/plugin-bundle.test.js.map +1 -0
  135. package/dist/plugin.d.ts +11 -0
  136. package/dist/plugin.d.ts.map +1 -0
  137. package/dist/plugin.js +46 -0
  138. package/dist/plugin.js.map +1 -0
  139. package/dist/quality-skills.test.d.ts +2 -0
  140. package/dist/quality-skills.test.d.ts.map +1 -0
  141. package/dist/quality-skills.test.js +141 -0
  142. package/dist/quality-skills.test.js.map +1 -0
  143. package/dist/report/footprint.d.ts +12 -0
  144. package/dist/report/footprint.d.ts.map +1 -0
  145. package/dist/report/footprint.js +121 -0
  146. package/dist/report/footprint.js.map +1 -0
  147. package/dist/report/ledger.d.ts +27 -0
  148. package/dist/report/ledger.d.ts.map +1 -0
  149. package/dist/report/ledger.js +197 -0
  150. package/dist/report/ledger.js.map +1 -0
  151. package/dist/report/oversight.d.ts +21 -0
  152. package/dist/report/oversight.d.ts.map +1 -0
  153. package/dist/report/oversight.js +146 -0
  154. package/dist/report/oversight.js.map +1 -0
  155. package/dist/report/policy-firing.test.d.ts +2 -0
  156. package/dist/report/policy-firing.test.d.ts.map +1 -0
  157. package/dist/report/policy-firing.test.js +68 -0
  158. package/dist/report/policy-firing.test.js.map +1 -0
  159. package/dist/report/scope.d.ts +64 -0
  160. package/dist/report/scope.d.ts.map +1 -0
  161. package/dist/report/scope.js +146 -0
  162. package/dist/report/scope.js.map +1 -0
  163. package/dist/who.d.ts +9 -0
  164. package/dist/who.d.ts.map +1 -0
  165. package/dist/who.js +11 -0
  166. package/dist/who.js.map +1 -0
  167. package/package.json +54 -4
  168. package/plugin/openagentsy.js +39478 -0
@@ -0,0 +1,141 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdtemp } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { loadRoles, selectQualitySkills, UI_DESIGN_FILES, UI_EXTENSIONS, } from '@openagentsy/core';
7
+ import { describe, expect, it } from 'vitest';
8
+ import { runBuild } from './commands/build.js';
9
+ import { renderPrompt } from './generate/prompt.js';
10
+ /** The roles file this repository ships. */
11
+ const SHIPPED_ROLES = fileURLToPath(new URL('../../../roles.yaml', import.meta.url));
12
+ /** The root of this repository. */
13
+ const REPO_ROOT = fileURLToPath(new URL('../../../', import.meta.url));
14
+ /** The roles that carry the routing. */
15
+ const ROUTED_ROLES = ['lead', 'builder'];
16
+ /**
17
+ * The measure: one token per word, per digit group and per punctuation
18
+ * mark, with the whitespace in front of a token carried by it.
19
+ *
20
+ * This is the shape of the cl100k_base pre-tokenizer for English prose:
21
+ * a word is one token, "user-facing" is three, ".css" is two, and a
22
+ * number is split into groups of three. The routing is measured with it
23
+ * and with the four-characters-per-token rule, so the claim holds under
24
+ * both.
25
+ */
26
+ function countTokens(text) {
27
+ const pattern = /\s*[A-Za-z]+(?:'[A-Za-z]+)*|\s*\d+|\s*[^\sA-Za-z0-9]/g;
28
+ let count = 0;
29
+ for (const match of text.match(pattern) ?? []) {
30
+ const trimmed = match.trim();
31
+ if (trimmed === '')
32
+ continue;
33
+ count += /^\d+$/.test(trimmed) ? Math.ceil(trimmed.length / 3) : 1;
34
+ }
35
+ return count;
36
+ }
37
+ /** The routing of one role. */
38
+ function routingOf(roles, name) {
39
+ const role = roles.roles[name];
40
+ if (!role)
41
+ throw new Error(`no ${name} role`);
42
+ if (role.skillRouting === undefined)
43
+ throw new Error(`the ${name} role has no routing`);
44
+ return role.skillRouting.trim();
45
+ }
46
+ describe('the quality routing', () => {
47
+ it('costs the Lead and the builder at most 80 tokens', async () => {
48
+ const roles = await loadRoles(SHIPPED_ROLES);
49
+ for (const name of ROUTED_ROLES) {
50
+ const routing = routingOf(roles, name);
51
+ expect(countTokens(routing)).toBeLessThanOrEqual(80);
52
+ expect(Math.ceil(routing.length / 4)).toBeLessThanOrEqual(80);
53
+ }
54
+ });
55
+ it('names the conditions the module implements', async () => {
56
+ const roles = await loadRoles(SHIPPED_ROLES);
57
+ const routing = routingOf(roles, 'lead');
58
+ for (const skill of ['oa-ui', 'oa-copy', 'oa-comments']) {
59
+ expect(routing).toContain(skill);
60
+ }
61
+ for (const extension of UI_EXTENSIONS) {
62
+ expect(routing).toContain(extension);
63
+ }
64
+ for (const designFile of UI_DESIGN_FILES) {
65
+ expect(routing).toContain(designFile);
66
+ }
67
+ expect(routing).toContain('user-facing copy');
68
+ expect(routing).toContain('agent prose');
69
+ expect(routing).toContain('comment');
70
+ });
71
+ it('gives the Lead and the builder the same routing', async () => {
72
+ const roles = await loadRoles(SHIPPED_ROLES);
73
+ expect(routingOf(roles, 'lead')).toBe(routingOf(roles, 'builder'));
74
+ });
75
+ it('keeps the routing off the roles that write no UI, copy or comments', async () => {
76
+ const roles = await loadRoles(SHIPPED_ROLES);
77
+ for (const name of ['scout', 'researcher', 'reviewer', 'advisor', 'archivist']) {
78
+ expect(roles.roles[name]?.skillRouting).toBeUndefined();
79
+ }
80
+ });
81
+ it('carries the routing into the prompt', async () => {
82
+ const roles = await loadRoles(SHIPPED_ROLES);
83
+ const lead = roles.roles.lead;
84
+ if (!lead)
85
+ throw new Error('no lead role');
86
+ const prompt = renderPrompt(lead);
87
+ expect(prompt).toContain('## Skills');
88
+ expect(prompt).toContain('oa-ui');
89
+ });
90
+ it('leaves the prompt of a role with no routing alone', async () => {
91
+ const roles = await loadRoles(SHIPPED_ROLES);
92
+ const reviewer = roles.roles.reviewer;
93
+ if (!reviewer)
94
+ throw new Error('no reviewer role');
95
+ expect(renderPrompt(reviewer)).not.toContain('## Skills');
96
+ });
97
+ });
98
+ describe('the selection the routing names', () => {
99
+ it('selects no skill for a change that touches no quality file', () => {
100
+ const skills = selectQualitySkills([
101
+ { path: 'packages/core/src/index.ts', lines: ['export const version = 1;'] },
102
+ ]);
103
+ expect(skills).toEqual([]);
104
+ });
105
+ it('selects oa-ui for a stylesheet change', () => {
106
+ const skills = selectQualitySkills([
107
+ { path: 'packages/app/src/app.css', lines: ['.card { padding: 1rem; }'] },
108
+ ]);
109
+ expect(skills).toEqual(['oa-ui']);
110
+ });
111
+ it('selects oa-ui and oa-copy for a component change', () => {
112
+ const skills = selectQualitySkills([
113
+ { path: 'packages/app/src/App.tsx', lines: ['<h1>Save</h1>'] },
114
+ ]);
115
+ expect(skills).toEqual(['oa-ui', 'oa-copy']);
116
+ });
117
+ it('selects oa-copy for a copy-bearing change', () => {
118
+ const skills = selectQualitySkills([{ path: 'README.md', lines: ['# The readme'] }]);
119
+ expect(skills).toEqual(['oa-copy']);
120
+ });
121
+ it('selects oa-comments for a comment-only change', () => {
122
+ const skills = selectQualitySkills([
123
+ { path: 'packages/core/src/index.ts', lines: ['// The version of the package.'] },
124
+ ]);
125
+ expect(skills).toEqual(['oa-comments']);
126
+ });
127
+ });
128
+ describe('the build', () => {
129
+ it('copies the reference files of a skill, and not only its SKILL.md', async () => {
130
+ const outDir = await mkdtemp(join(tmpdir(), 'oa-skills-'));
131
+ const result = await runBuild({ workspace: REPO_ROOT, stateRoot: REPO_ROOT, outDir });
132
+ expect(result.skills).toContain('oa-ui');
133
+ expect(result.skills).toContain('oa-copy');
134
+ expect(result.skills).toContain('oa-comments');
135
+ expect(existsSync(join(outDir, 'skills', 'oa-ui', 'SKILL.md'))).toBe(true);
136
+ expect(existsSync(join(outDir, 'skills', 'oa-ui', 'references', 'purpose-and-dials.md'))).toBe(true);
137
+ expect(existsSync(join(outDir, 'skills', 'oa-copy', 'references', 'prose.md'))).toBe(true);
138
+ expect(existsSync(join(outDir, 'skills', 'oa-comments', 'references', 'tells.md'))).toBe(true);
139
+ });
140
+ });
141
+ //# sourceMappingURL=quality-skills.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-skills.test.js","sourceRoot":"","sources":["../src/quality-skills.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { FootprintFacts } from '@openagentsy/core';
2
+ import type { LedgerDb } from './scope.js';
3
+ /**
4
+ * The footprint of the scope.
5
+ *
6
+ * The rows are the estimates of the messages the scope selects, and the
7
+ * bounds of the scope are the sums of the bounds of the rows. The table
8
+ * records no grid, so the report says the grid is not recorded rather than
9
+ * naming one it cannot read.
10
+ */
11
+ export declare function footprintFacts(db: LedgerDb | null): FootprintFacts;
12
+ //# sourceMappingURL=footprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footprint.d.ts","sourceRoot":"","sources":["../../src/report/footprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoE3C;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc,CA0ClE"}
@@ -0,0 +1,121 @@
1
+ import { providerDefaults } from '@openagentsy/footprint';
2
+ /**
3
+ * The footprint of the scope.
4
+ *
5
+ * The numbers are the ledger's `footprint` table: the estimate the
6
+ * estimator produced, with the ends of the interval it produced it with and
7
+ * the method that produced it. The table is the only place the estimate
8
+ * lives, so the report reads it and computes nothing of its own.
9
+ */
10
+ /** A number, when it is one. */
11
+ function n(value) {
12
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
13
+ }
14
+ /** An interval, when both of its ends are numbers. */
15
+ function rangeOf(lo, hi) {
16
+ if (typeof lo !== 'number' || typeof hi !== 'number')
17
+ return null;
18
+ if (!Number.isFinite(lo) || !Number.isFinite(hi))
19
+ return null;
20
+ return [Math.min(lo, hi), Math.max(lo, hi)];
21
+ }
22
+ /** How wide an interval is, relative to its low end. */
23
+ function width(range) {
24
+ return (range[1] - range[0]) / Math.max(range[0], 1e-12);
25
+ }
26
+ /**
27
+ * The input the answer leans on hardest.
28
+ *
29
+ * The rule is the one the estimator uses: the energy widens with the size
30
+ * of the model, the water with the site and the carbon with the grid, and
31
+ * the widest interval relative to its low end decides. The ledger stores
32
+ * the bounds and not the inputs, so the rule is applied to the bounds. An
33
+ * estimate whose bounds are all exact has nothing that dominates, and the
34
+ * report says so rather than naming a winner.
35
+ */
36
+ function dominant(energy, water, co2) {
37
+ const scores = [
38
+ ['model-size', energy === null ? 0 : width(energy)],
39
+ ['site', water === null ? 0 : width(water)],
40
+ ['grid', co2 === null ? 0 : width(co2)],
41
+ ];
42
+ scores.sort((a, b) => b[1] - a[1]);
43
+ const top = scores[0];
44
+ if (top === undefined || top[1] <= 0)
45
+ return null;
46
+ return top[0];
47
+ }
48
+ /** The facts of a scope with no footprint estimate. */
49
+ function emptyFacts() {
50
+ return {
51
+ present: false,
52
+ energyKwh: null,
53
+ waterL: null,
54
+ co2Kg: null,
55
+ method: null,
56
+ dominantUncertainty: null,
57
+ grid: null,
58
+ undisclosedProviders: 0,
59
+ };
60
+ }
61
+ /**
62
+ * The footprint of the scope.
63
+ *
64
+ * The rows are the estimates of the messages the scope selects, and the
65
+ * bounds of the scope are the sums of the bounds of the rows. The table
66
+ * records no grid, so the report says the grid is not recorded rather than
67
+ * naming one it cannot read.
68
+ */
69
+ export function footprintFacts(db) {
70
+ if (db === null)
71
+ return emptyFacts();
72
+ const row = db
73
+ .query(`SELECT
74
+ SUM(f.energy_lo) AS energy_lo, SUM(f.energy_hi) AS energy_hi,
75
+ SUM(f.water_lo) AS water_lo, SUM(f.water_hi) AS water_hi,
76
+ SUM(f.co2_lo) AS co2_lo, SUM(f.co2_hi) AS co2_hi,
77
+ COUNT(*) AS rows
78
+ FROM main.footprint f
79
+ JOIN temp.usage u ON u.message_id = f.message_id`)
80
+ .get();
81
+ const rows = n(row?.rows);
82
+ if (rows === 0) {
83
+ const providers = providersOf(db);
84
+ return { ...emptyFacts(), undisclosedProviders: undisclosed(providers) };
85
+ }
86
+ const energy = rangeOf(row?.energy_lo, row?.energy_hi);
87
+ const water = rangeOf(row?.water_lo, row?.water_hi);
88
+ const co2 = rangeOf(row?.co2_lo, row?.co2_hi);
89
+ const method = db
90
+ .query(`SELECT DISTINCT f.method_version AS method
91
+ FROM main.footprint f
92
+ JOIN temp.usage u ON u.message_id = f.message_id`)
93
+ .all();
94
+ return {
95
+ present: true,
96
+ energyKwh: energy === null ? null : [energy[0] / 1000, energy[1] / 1000],
97
+ waterL: water,
98
+ co2Kg: co2,
99
+ method: method[0]?.method ?? null,
100
+ dominantUncertainty: dominant(energy, water, co2),
101
+ grid: null,
102
+ undisclosedProviders: undisclosed(providersOf(db)),
103
+ };
104
+ }
105
+ /** The providers the scope used. */
106
+ function providersOf(db) {
107
+ const rows = db.query('SELECT DISTINCT provider FROM temp.usage').all();
108
+ return rows.map((row) => row.provider);
109
+ }
110
+ /**
111
+ * The providers that do not disclose their infrastructure.
112
+ *
113
+ * The disclosure is the footprint package's table of what a provider says
114
+ * about its own site: a provider that is not in the table discloses
115
+ * nothing, and the report says how many of them there are rather than
116
+ * leaving the reader to assume every number is known.
117
+ */
118
+ function undisclosed(providers) {
119
+ return providers.filter((provider) => !providerDefaults(provider).disclosed).length;
120
+ }
121
+ //# sourceMappingURL=footprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footprint.js","sourceRoot":"","sources":["../../src/report/footprint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { CostFacts, PolicyFacts, ReportScope, ValueFacts, WasteFacts } from '@openagentsy/core';
2
+ import { footprintFacts } from './footprint.js';
3
+ /**
4
+ * The ledger, as the report reads it.
5
+ *
6
+ * Every number here is the ledger's own query, run over the rows the scope
7
+ * selects. The report never totals a thing itself: it asks the ledger, and
8
+ * the ledger answers.
9
+ */
10
+ /** The facts the ledger gives the report. */
11
+ export type LedgerFacts = {
12
+ cost: CostFacts;
13
+ waste: WasteFacts;
14
+ value: ValueFacts;
15
+ /** The footprint of the scope, from the ledger's footprint table. */
16
+ footprint: ReturnType<typeof footprintFacts>;
17
+ /** The policies of the scope, from the ledger's policy firing table. */
18
+ policies: PolicyFacts;
19
+ };
20
+ /**
21
+ * The facts of the scope, from the ledger.
22
+ *
23
+ * A project with no ledger has nothing to show, and reading it must not
24
+ * create one: the file is only opened when it is there.
25
+ */
26
+ export declare function ledgerFacts(stateRoot: string, scope: ReportScope): Promise<LedgerFacts>;
27
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/report/ledger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACX,MAAM,mBAAmB,CAAC;AAoB3B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD;;;;;;GAMG;AAEH,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,qEAAqE;IACrE,SAAS,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAoLF;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAqB7F"}
@@ -0,0 +1,197 @@
1
+ import { access } from 'node:fs/promises';
2
+ import { FIRST_PARTY_POLICY_IDS } from '@openagentsy/hooks';
3
+ import { closeLedger, ledgerPath, ledgerTotals, offloadSavings, openLedger, outcomeTotals, perRoleTotals, policyFireCounts, policyFiringTotal, priceMessage, tokensPerResult, topPoliciesByFireCount, wasteByKind, wasteTotals, } from '@openagentsy/ledger';
4
+ import { footprintFacts } from './footprint.js';
5
+ import { shadowScope, windowDays } from './scope.js';
6
+ /**
7
+ * A price, from the cache and never from the network.
8
+ *
9
+ * The report must not use the network, so the fetch always fails and the
10
+ * cached snapshot is what is left. A price nobody cached is no price.
11
+ */
12
+ const offlineFetch = async () => {
13
+ throw new Error('the report does not use the network');
14
+ };
15
+ /**
16
+ * The price of some tokens.
17
+ *
18
+ * The ledger asks the caller what the tokens would have cost on the cloud,
19
+ * because only the caller knows which model the offloaded work replaced.
20
+ * The report does not know it either, so the price comes from the cached
21
+ * price of the model that ran the work, and a local run has no price at
22
+ * all: the cloud price stays empty rather than pretending the work was
23
+ * free.
24
+ */
25
+ const offlinePrice = async (provider, model, tokens) => {
26
+ const result = await priceMessage(provider, model, tokens, { fetchImpl: offlineFetch });
27
+ return result.cost;
28
+ };
29
+ /** A number, when it is one. */
30
+ function n(value) {
31
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
32
+ }
33
+ /** A number that may be absent. */
34
+ function optional(value) {
35
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
36
+ }
37
+ /**
38
+ * The cost of the scope.
39
+ *
40
+ * The tokens and the roles are the ledger's totals, and the split between
41
+ * the spend the host reported and the spend the pricing chain estimated is
42
+ * read from the same table the totals come from.
43
+ */
44
+ function costOf(db) {
45
+ const totals = ledgerTotals(db);
46
+ const roles = perRoleTotals(db);
47
+ const split = db
48
+ .query(`SELECT
49
+ SUM(cost_reported) AS reported,
50
+ SUM(cost_estimated) AS estimated,
51
+ COUNT(DISTINCT CASE
52
+ WHEN cost_reported IS NULL AND cost_estimated IS NULL
53
+ THEN provider || '|' || model END) AS unpriced_models,
54
+ SUM(CASE
55
+ WHEN cost_reported IS NULL AND cost_estimated IS NULL THEN 1 ELSE 0 END) AS unpriced_messages
56
+ FROM usage`)
57
+ .get();
58
+ return {
59
+ present: totals.messages > 0,
60
+ messages: totals.messages,
61
+ tokens: totals.tokens,
62
+ roles: roles.map((row) => ({
63
+ role: row.role,
64
+ tokens: row.tokens,
65
+ usd: row.usd,
66
+ unpriced: row.unpriced,
67
+ })),
68
+ reportedUsd: optional(split?.reported),
69
+ estimatedUsd: optional(split?.estimated),
70
+ unpricedModels: n(split?.unpriced_models),
71
+ unpricedMessages: n(split?.unpriced_messages),
72
+ };
73
+ }
74
+ /** The waste of the scope. */
75
+ function wasteOf(db) {
76
+ const totals = wasteTotals(db);
77
+ const byKind = wasteByKind(db);
78
+ return {
79
+ present: byKind.length > 0,
80
+ tokens: totals.tokens,
81
+ percentage: totals.percentage,
82
+ byKind: byKind.map((row) => ({
83
+ kind: row.kind,
84
+ count: row.count,
85
+ tokens: row.tokens,
86
+ percentage: row.percentage,
87
+ })),
88
+ };
89
+ }
90
+ /** The value of the scope. */
91
+ async function worthOf(db, scope) {
92
+ const outcomes = outcomeTotals(db);
93
+ const results = tokensPerResult(db, windowDays(scope));
94
+ const offload = await offloadSavings(db, offlinePrice);
95
+ return {
96
+ present: outcomes.tasksCompleted + outcomes.tasksScrapped + outcomes.commits + outcomes.prsMerged > 0,
97
+ tasksCompleted: outcomes.tasksCompleted,
98
+ tasksScrapped: outcomes.tasksScrapped,
99
+ commits: outcomes.commits,
100
+ prsMerged: outcomes.prsMerged,
101
+ tokensPerTask: results.tokensPerTask,
102
+ tokensPerCommit: results.tokensPerCommit,
103
+ offloadMessages: offload.messages,
104
+ offloadTokens: offload.tokens,
105
+ offloadUsd: offload.usd,
106
+ offloadCloudUsd: offload.cloudUsd,
107
+ offloadSavedUsd: offload.savedUsd,
108
+ };
109
+ }
110
+ /**
111
+ * The policies of the scope.
112
+ *
113
+ * The ten policies with the most firings are what the report shows, and the
114
+ * first-party policies that never fired are named: a policy that never fires
115
+ * is dead code or a coverage gap, and a reader cannot tell which one it is
116
+ * without being told.
117
+ */
118
+ function policiesOf(db) {
119
+ const counts = topPoliciesByFireCount(db, 10);
120
+ const fired = new Set(policyFireCounts(db).map((entry) => entry.policyId));
121
+ return {
122
+ present: policyFiringTotal(db) > 0,
123
+ firings: policyFiringTotal(db),
124
+ top: counts.map((entry) => ({
125
+ policyId: entry.policyId,
126
+ count: entry.count,
127
+ blocks: entry.blocks,
128
+ flags: entry.flags,
129
+ versions: entry.versions,
130
+ })),
131
+ neverFired: FIRST_PARTY_POLICY_IDS.filter((id) => !fired.has(id)),
132
+ };
133
+ }
134
+ /** The facts of a project with no ledger. */
135
+ function emptyFacts() {
136
+ return {
137
+ cost: {
138
+ present: false,
139
+ messages: 0,
140
+ tokens: 0,
141
+ roles: [],
142
+ reportedUsd: null,
143
+ estimatedUsd: null,
144
+ unpricedModels: 0,
145
+ unpricedMessages: 0,
146
+ },
147
+ waste: { present: false, tokens: 0, percentage: 0, byKind: [] },
148
+ value: {
149
+ present: false,
150
+ tasksCompleted: 0,
151
+ tasksScrapped: 0,
152
+ commits: 0,
153
+ prsMerged: 0,
154
+ tokensPerTask: null,
155
+ tokensPerCommit: null,
156
+ offloadMessages: 0,
157
+ offloadTokens: 0,
158
+ offloadUsd: null,
159
+ offloadCloudUsd: null,
160
+ offloadSavedUsd: null,
161
+ },
162
+ footprint: footprintFacts(null),
163
+ policies: {
164
+ present: false,
165
+ firings: 0,
166
+ top: [],
167
+ neverFired: [...FIRST_PARTY_POLICY_IDS],
168
+ },
169
+ };
170
+ }
171
+ /**
172
+ * The facts of the scope, from the ledger.
173
+ *
174
+ * A project with no ledger has nothing to show, and reading it must not
175
+ * create one: the file is only opened when it is there.
176
+ */
177
+ export async function ledgerFacts(stateRoot, scope) {
178
+ const path = ledgerPath(stateRoot);
179
+ const exists = await access(path).then(() => true, () => false);
180
+ if (!exists)
181
+ return emptyFacts();
182
+ const db = openLedger(stateRoot);
183
+ try {
184
+ shadowScope(db, scope);
185
+ return {
186
+ cost: costOf(db),
187
+ waste: wasteOf(db),
188
+ value: await worthOf(db, scope),
189
+ footprint: footprintFacts(db),
190
+ policies: policiesOf(db),
191
+ };
192
+ }
193
+ finally {
194
+ closeLedger(db);
195
+ }
196
+ }
197
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/report/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type OversightFacts, type ReportScope } from '@openagentsy/core';
2
+ import type { Finding, TaskFindings } from '@openagentsy/quality';
3
+ /**
4
+ * The findings of the scope.
5
+ *
6
+ * The findings are the store's, read through the quality package's read
7
+ * path. A bean scope reads the findings of that task, because the ledger
8
+ * reads the task of a bean the same way. Every other scope reads them all:
9
+ * the findings store has no session and no window, and a report that
10
+ * narrowed it by guesswork would be narrowing it wrongly.
11
+ */
12
+ export declare function scopedFindings(findings: TaskFindings, scope: ReportScope): Finding[];
13
+ /**
14
+ * The oversight of the scope.
15
+ *
16
+ * The board records of a bean scope are the records of that bean, and every
17
+ * other scope sees the whole board: the board has no session column and no
18
+ * window, so a report that narrowed it would be narrowing it by guesswork.
19
+ */
20
+ export declare function oversightFacts(stateRoot: string, scope: ReportScope, findings: TaskFindings): Promise<OversightFacts>;
21
+ //# sourceMappingURL=oversight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oversight.d.ts","sourceRoot":"","sources":["../../src/report/oversight.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,cAAc,EAEnB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AA8ElE;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,EAAE,CAOpF;AA6BD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC,cAAc,CAAC,CAwBzB"}