@openagentsy/ledger 0.0.0-stage → 0.2.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.
- package/LICENSE +674 -0
- package/README.md +20 -2
- package/data/pricing/litellm.json +8882 -0
- package/data/pricing/models.dev.json +12151 -0
- package/data/pricing/openrouter.json +1017 -0
- package/dist/db.d.ts +44 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +97 -0
- package/dist/db.js.map +1 -0
- package/dist/export.d.ts +71 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +78 -0
- package/dist/export.js.map +1 -0
- package/dist/footprint.d.ts +24 -0
- package/dist/footprint.d.ts.map +1 -0
- package/dist/footprint.js +33 -0
- package/dist/footprint.js.map +1 -0
- package/dist/git.d.ts +44 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +126 -0
- package/dist/git.js.map +1 -0
- package/dist/git.test.d.ts +2 -0
- package/dist/git.test.d.ts.map +1 -0
- package/dist/git.test.js +101 -0
- package/dist/git.test.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest.d.ts +133 -0
- package/dist/ingest.d.ts.map +1 -0
- package/dist/ingest.js +324 -0
- package/dist/ingest.js.map +1 -0
- package/dist/ingest.test.d.ts +2 -0
- package/dist/ingest.test.d.ts.map +1 -0
- package/dist/ingest.test.js +245 -0
- package/dist/ingest.test.js.map +1 -0
- package/dist/outcomes.d.ts +81 -0
- package/dist/outcomes.d.ts.map +1 -0
- package/dist/outcomes.js +85 -0
- package/dist/outcomes.js.map +1 -0
- package/dist/outcomes.test.d.ts +2 -0
- package/dist/outcomes.test.d.ts.map +1 -0
- package/dist/outcomes.test.js +160 -0
- package/dist/outcomes.test.js.map +1 -0
- package/dist/policy.d.ts +48 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +99 -0
- package/dist/policy.js.map +1 -0
- package/dist/policy.test.d.ts +2 -0
- package/dist/policy.test.d.ts.map +1 -0
- package/dist/policy.test.js +126 -0
- package/dist/policy.test.js.map +1 -0
- package/dist/pricing.d.ts +92 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +341 -0
- package/dist/pricing.js.map +1 -0
- package/dist/pricing.test.d.ts +2 -0
- package/dist/pricing.test.d.ts.map +1 -0
- package/dist/pricing.test.js +167 -0
- package/dist/pricing.test.js.map +1 -0
- package/dist/query.d.ts +134 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +247 -0
- package/dist/query.js.map +1 -0
- package/dist/query.test.d.ts +2 -0
- package/dist/query.test.d.ts.map +1 -0
- package/dist/query.test.js +242 -0
- package/dist/query.test.js.map +1 -0
- package/dist/schema.d.ts +33 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +139 -0
- package/dist/schema.js.map +1 -0
- package/dist/schema.test.d.ts +2 -0
- package/dist/schema.test.d.ts.map +1 -0
- package/dist/schema.test.js +129 -0
- package/dist/schema.test.js.map +1 -0
- package/dist/waste.d.ts +116 -0
- package/dist/waste.d.ts.map +1 -0
- package/dist/waste.js +553 -0
- package/dist/waste.js.map +1 -0
- package/dist/waste.test.d.ts +2 -0
- package/dist/waste.test.d.ts.map +1 -0
- package/dist/waste.test.js +278 -0
- package/dist/waste.test.js.map +1 -0
- package/package.json +52 -4
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
6
|
+
import { closeLedger, openLedger } from './db.js';
|
|
7
|
+
import { backfillFromOpencode, normalizeOcMessage, recordAssistantMessage, recordWaste, } from './ingest.js';
|
|
8
|
+
import { perRoleTotals } from './query.js';
|
|
9
|
+
let root;
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = await mkdtemp(join(tmpdir(), 'openagentsy-ingest-'));
|
|
12
|
+
});
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await rm(root, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
const CTX = {
|
|
17
|
+
rootSessionId: 'ses_root',
|
|
18
|
+
role: 'builder',
|
|
19
|
+
latencyMs: 1200,
|
|
20
|
+
activeTaskId: 'oa-2b-1',
|
|
21
|
+
ts: '2026-10-26T10:00:00.000Z',
|
|
22
|
+
};
|
|
23
|
+
const MESSAGE = {
|
|
24
|
+
id: 'msg_1',
|
|
25
|
+
sessionID: 'ses_child',
|
|
26
|
+
providerID: 'anthropic',
|
|
27
|
+
modelID: 'claude-sonnet-4-5',
|
|
28
|
+
tokens: { input: 100, output: 40, reasoning: 10, cache: { read: 900, write: 5 } },
|
|
29
|
+
cost: 0.25,
|
|
30
|
+
};
|
|
31
|
+
describe('the normalizer', () => {
|
|
32
|
+
it('reads the flat shape of V1', () => {
|
|
33
|
+
const message = normalizeOcMessage(MESSAGE);
|
|
34
|
+
expect(message.id).toBe('msg_1');
|
|
35
|
+
expect(message.providerID).toBe('anthropic');
|
|
36
|
+
expect(message.modelID).toBe('claude-sonnet-4-5');
|
|
37
|
+
expect(message.tokens?.cache?.read).toBe(900);
|
|
38
|
+
});
|
|
39
|
+
it('reads the nested shape of V2', () => {
|
|
40
|
+
const message = normalizeOcMessage({
|
|
41
|
+
id: 'msg_2',
|
|
42
|
+
sessionID: 'ses_child',
|
|
43
|
+
model: { providerID: 'anthropic', id: 'claude-sonnet-4-5' },
|
|
44
|
+
tokens: { input: 100, output: 40, reasoning: 10, cache: { read: 900, write: 5 } },
|
|
45
|
+
cost: 0.25,
|
|
46
|
+
});
|
|
47
|
+
expect(message.id).toBe('msg_2');
|
|
48
|
+
expect(message.providerID).toBe('anthropic');
|
|
49
|
+
expect(message.modelID).toBe('claude-sonnet-4-5');
|
|
50
|
+
expect(message.tokens?.input).toBe(100);
|
|
51
|
+
});
|
|
52
|
+
it('reads a message nested under a message', () => {
|
|
53
|
+
const message = normalizeOcMessage({
|
|
54
|
+
message: {
|
|
55
|
+
id: 'msg_3',
|
|
56
|
+
sessionID: 'ses_child',
|
|
57
|
+
model: { providerID: 'ollama-cloud', id: 'beam' },
|
|
58
|
+
tokens: { input: 1, output: 2 },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
expect(message.id).toBe('msg_3');
|
|
62
|
+
expect(message.providerID).toBe('ollama-cloud');
|
|
63
|
+
expect(message.modelID).toBe('beam');
|
|
64
|
+
});
|
|
65
|
+
it('refuses a message without an id', () => {
|
|
66
|
+
expect(() => normalizeOcMessage({ providerID: 'anthropic' })).toThrow(/no id/);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('the ingest', () => {
|
|
70
|
+
it('writes one row per message', () => {
|
|
71
|
+
const db = openLedger(root);
|
|
72
|
+
try {
|
|
73
|
+
recordAssistantMessage(db, MESSAGE, CTX);
|
|
74
|
+
recordAssistantMessage(db, MESSAGE, CTX);
|
|
75
|
+
const rows = db.query('SELECT COUNT(*) AS count FROM usage').get();
|
|
76
|
+
expect(rows.count).toBe(1);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
closeLedger(db);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
it('stores the role it is given, without resolving it', () => {
|
|
83
|
+
const db = openLedger(root);
|
|
84
|
+
try {
|
|
85
|
+
recordAssistantMessage(db, MESSAGE, { ...CTX, role: 'agentsy' });
|
|
86
|
+
const row = db.query('SELECT role, role_raw FROM usage').get();
|
|
87
|
+
expect(row.role).toBe('agentsy');
|
|
88
|
+
expect(row.role_raw).toBe(null);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
closeLedger(db);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
it('counts the reasoning once for a provider that folds it into the output', () => {
|
|
95
|
+
const db = openLedger(root);
|
|
96
|
+
try {
|
|
97
|
+
recordAssistantMessage(db, { ...MESSAGE, id: 'msg_codex', providerID: 'codex' }, CTX);
|
|
98
|
+
const row = db
|
|
99
|
+
.query('SELECT output, reasoning FROM usage WHERE message_id = ?')
|
|
100
|
+
.get('msg_codex');
|
|
101
|
+
expect(row.output).toBe(30);
|
|
102
|
+
expect(row.reasoning).toBe(10);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
closeLedger(db);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
it('leaves the cost empty when the host reported none', () => {
|
|
109
|
+
const db = openLedger(root);
|
|
110
|
+
try {
|
|
111
|
+
const { cost: _cost, ...withoutCost } = MESSAGE;
|
|
112
|
+
recordAssistantMessage(db, withoutCost, CTX);
|
|
113
|
+
const row = db.query('SELECT cost_reported, cost_estimated FROM usage').get();
|
|
114
|
+
expect(row.cost_reported).toBe(null);
|
|
115
|
+
expect(row.cost_estimated).toBe(null);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
closeLedger(db);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
it('redacts a secret in a waste detail', () => {
|
|
122
|
+
const db = openLedger(root);
|
|
123
|
+
try {
|
|
124
|
+
const secret = ['sk', 'abcdefghijklmnopqrstuvwx'].join('-');
|
|
125
|
+
recordWaste(db, {
|
|
126
|
+
rootSessionId: 'ses_root',
|
|
127
|
+
kind: 'retry',
|
|
128
|
+
tokens: 10,
|
|
129
|
+
detail: `the key was ${secret}`,
|
|
130
|
+
ts: CTX.ts,
|
|
131
|
+
});
|
|
132
|
+
const row = db.query('SELECT detail FROM waste').get();
|
|
133
|
+
expect(row.detail).not.toContain(secret);
|
|
134
|
+
expect(row.detail).toContain('redacted');
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
closeLedger(db);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe('the backfill', () => {
|
|
142
|
+
async function fixture(dataDir) {
|
|
143
|
+
await mkdir(dataDir, { recursive: true });
|
|
144
|
+
const source = new Database(join(dataDir, 'opencode.db'), { create: true });
|
|
145
|
+
source.exec('CREATE TABLE message (id text PRIMARY KEY, session_id text NOT NULL, time_created integer NOT NULL, data text NOT NULL)');
|
|
146
|
+
source.exec('CREATE TABLE session (id text PRIMARY KEY, parent_id text)');
|
|
147
|
+
source.query('INSERT INTO session (id, parent_id) VALUES (?, ?)').run('ses_root', null);
|
|
148
|
+
source.query('INSERT INTO session (id, parent_id) VALUES (?, ?)').run('ses_child', 'ses_root');
|
|
149
|
+
source.query('INSERT INTO message (id, session_id, time_created, data) VALUES (?,?,?,?)').run('msg_old', 'ses_child', 1_700_000_000_000, JSON.stringify({
|
|
150
|
+
role: 'assistant',
|
|
151
|
+
agent: 'oa-builder',
|
|
152
|
+
providerID: 'anthropic',
|
|
153
|
+
modelID: 'claude-sonnet-4-5',
|
|
154
|
+
tokens: { input: 10, output: 20, reasoning: 0, cache: { read: 5, write: 1 } },
|
|
155
|
+
cost: 0.5,
|
|
156
|
+
}));
|
|
157
|
+
// A provider of the Codex family folds the reasoning into the output.
|
|
158
|
+
source.query('INSERT INTO message (id, session_id, time_created, data) VALUES (?,?,?,?)').run('msg_codex', 'ses_child', 1_600_000_000_000, JSON.stringify({
|
|
159
|
+
role: 'assistant',
|
|
160
|
+
agent: 'oa-builder',
|
|
161
|
+
providerID: 'codex',
|
|
162
|
+
modelID: 'gpt-codex',
|
|
163
|
+
tokens: { input: 5, output: 40, reasoning: 10, cache: { read: 0, write: 0 } },
|
|
164
|
+
}));
|
|
165
|
+
source.close();
|
|
166
|
+
}
|
|
167
|
+
it('is off unless it is asked for', async () => {
|
|
168
|
+
const dataDir = join(root, 'opencode');
|
|
169
|
+
await fixture(dataDir);
|
|
170
|
+
const db = openLedger(root);
|
|
171
|
+
try {
|
|
172
|
+
const result = backfillFromOpencode(db, { dataDir });
|
|
173
|
+
expect(result.enabled).toBe(false);
|
|
174
|
+
expect(result.imported).toBe(0);
|
|
175
|
+
const rows = db.query('SELECT COUNT(*) AS count FROM usage').get();
|
|
176
|
+
expect(rows.count).toBe(0);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
closeLedger(db);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
it('imports the past messages with the role unresolved', async () => {
|
|
183
|
+
const dataDir = join(root, 'opencode');
|
|
184
|
+
await fixture(dataDir);
|
|
185
|
+
const db = openLedger(root);
|
|
186
|
+
try {
|
|
187
|
+
const result = backfillFromOpencode(db, { enabled: true, dataDir });
|
|
188
|
+
expect(result.errors).toEqual([]);
|
|
189
|
+
expect(result.imported).toBe(2);
|
|
190
|
+
const row = db.query('SELECT role, role_raw, root_session_id, input FROM usage').get();
|
|
191
|
+
expect(row.role).toBe(null);
|
|
192
|
+
expect(row.role_raw).toBe('oa-builder');
|
|
193
|
+
expect(row.root_session_id).toBe('ses_root');
|
|
194
|
+
expect(row.input).toBe(10);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
closeLedger(db);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
it('counts the reasoning once for a backfilled codex message', async () => {
|
|
201
|
+
const dataDir = join(root, 'opencode');
|
|
202
|
+
await fixture(dataDir);
|
|
203
|
+
const db = openLedger(root);
|
|
204
|
+
try {
|
|
205
|
+
backfillFromOpencode(db, { enabled: true, dataDir });
|
|
206
|
+
const row = db
|
|
207
|
+
.query('SELECT output, reasoning FROM usage WHERE message_id = ?')
|
|
208
|
+
.get('msg_codex');
|
|
209
|
+
expect(row.reasoning).toBe(10);
|
|
210
|
+
expect(row.output).toBe(30);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
closeLedger(db);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
it('leaves an imported row out of the per-role report', async () => {
|
|
217
|
+
const dataDir = join(root, 'opencode');
|
|
218
|
+
await fixture(dataDir);
|
|
219
|
+
const db = openLedger(root);
|
|
220
|
+
try {
|
|
221
|
+
backfillFromOpencode(db, { enabled: true, dataDir });
|
|
222
|
+
expect(perRoleTotals(db)).toEqual([]);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
closeLedger(db);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
it('refuses a file whose tables differ, and says what it found', async () => {
|
|
229
|
+
const dataDir = join(root, 'opencode');
|
|
230
|
+
await mkdir(dataDir, { recursive: true });
|
|
231
|
+
const source = new Database(join(dataDir, 'opencode.db'), { create: true });
|
|
232
|
+
source.exec('CREATE TABLE blobs (id text PRIMARY KEY, value text)');
|
|
233
|
+
source.close();
|
|
234
|
+
const db = openLedger(root);
|
|
235
|
+
try {
|
|
236
|
+
const result = backfillFromOpencode(db, { enabled: true, dataDir });
|
|
237
|
+
expect(result.imported).toBe(0);
|
|
238
|
+
expect(result.errors[0]).toMatch(/no "message" table/);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
closeLedger(db);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
//# sourceMappingURL=ingest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.test.js","sourceRoot":"","sources":["../src/ingest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import { type OutcomeKind } from './schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* The outcomes of §6.5 — what the tokens of the work produced.
|
|
5
|
+
*
|
|
6
|
+
* Three sources fill the outcome table: the task transitions of the tasks
|
|
7
|
+
* store, the `Refs:` trailers of the commits and the merges of the pull
|
|
8
|
+
* requests. The first two are scanned here; the third is seen by the `pr`
|
|
9
|
+
* integration mode.
|
|
10
|
+
*
|
|
11
|
+
* The two scanners that read another package's data take it through an
|
|
12
|
+
* injected reader instead of importing it: the ledger holds no dependency on
|
|
13
|
+
* the tasks store, and the caller that knows the store injects it.
|
|
14
|
+
*/
|
|
15
|
+
/** An outcome, ready to be written. */
|
|
16
|
+
export type OutcomeCandidate = {
|
|
17
|
+
kind: OutcomeKind;
|
|
18
|
+
ts: string;
|
|
19
|
+
taskId?: string | undefined;
|
|
20
|
+
commitSha?: string | undefined;
|
|
21
|
+
rootSessionId?: string | undefined;
|
|
22
|
+
};
|
|
23
|
+
/** What a caller hands to `recordOutcome`. */
|
|
24
|
+
export type OutcomeInput = {
|
|
25
|
+
kind: OutcomeKind;
|
|
26
|
+
taskId?: string | undefined;
|
|
27
|
+
commitSha?: string | undefined;
|
|
28
|
+
rootSessionId?: string | undefined;
|
|
29
|
+
/** The moment the outcome happened; the clock when it is not given. */
|
|
30
|
+
ts?: string | undefined;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Record one outcome.
|
|
34
|
+
*
|
|
35
|
+
* The kind is checked against the four of §6.1 before the row is written, so
|
|
36
|
+
* a mistake is a message and not a constraint violation. The write is
|
|
37
|
+
* idempotent: an outcome that is already there — the same kind, the same
|
|
38
|
+
* task and the same commit — is not written twice, because the scanners are
|
|
39
|
+
* commands that run over the same history again and again.
|
|
40
|
+
*
|
|
41
|
+
* Returns whether a row was written.
|
|
42
|
+
*/
|
|
43
|
+
export declare function recordOutcome(db: Database, outcome: OutcomeInput): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The outcomes of the commits that carry a `Refs:` trailer (§6.5).
|
|
46
|
+
*
|
|
47
|
+
* The trailer is what the pre-commit hook requires of an agent commit, so
|
|
48
|
+
* the commits of the agents are the ones this finds. A commit without a
|
|
49
|
+
* trailer names no task and becomes no outcome.
|
|
50
|
+
*/
|
|
51
|
+
export declare function scanRefsTrailers(gitDir: string, since: string): OutcomeCandidate[];
|
|
52
|
+
/**
|
|
53
|
+
* A task, as the transition scan needs to see it.
|
|
54
|
+
*
|
|
55
|
+
* The shape is the one the tasks store has, and only the three fields the
|
|
56
|
+
* scan reads: the id, the status and the moment the task last changed.
|
|
57
|
+
*/
|
|
58
|
+
export type LedgerTask = {
|
|
59
|
+
id: string;
|
|
60
|
+
status: string;
|
|
61
|
+
updated_at?: string | undefined;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The reader of the tasks store.
|
|
65
|
+
*
|
|
66
|
+
* The ledger does not import the tasks package: the store is another
|
|
67
|
+
* program's data, and the caller that knows where it lives injects a reader
|
|
68
|
+
* for it. The reader is given the root it is to read and hands back the
|
|
69
|
+
* tasks it found.
|
|
70
|
+
*/
|
|
71
|
+
export type TaskReader = (root: string) => Promise<LedgerTask[]>;
|
|
72
|
+
/**
|
|
73
|
+
* The outcomes of the task transitions (§6.5).
|
|
74
|
+
*
|
|
75
|
+
* A task that reached `completed` or `scrapped` is an outcome. The moment of
|
|
76
|
+
* the transition is the moment the task last changed; a task that carries no
|
|
77
|
+
* timestamp is taken as it is now, because the store says its status and
|
|
78
|
+
* the time of the change is not in it.
|
|
79
|
+
*/
|
|
80
|
+
export declare function scanTaskTransitions(root: string, since: string, reader: TaskReader): Promise<OutcomeCandidate[]>;
|
|
81
|
+
//# sourceMappingURL=outcomes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.d.ts","sourceRoot":"","sources":["../src/outcomes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;;;;GAWG;AAEH,uCAAuC;AACvC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAEF,8CAA8C;AAC9C,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,uEAAuE;IACvE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB,CAAC;AAMF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAiB1E;AAcD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAalF;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAc7B"}
|
package/dist/outcomes.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { withBusyRetry } from './db.js';
|
|
2
|
+
import { gitCommits, refsTrailers } from './git.js';
|
|
3
|
+
import { OUTCOME_KINDS } from './schema.js';
|
|
4
|
+
const OUTCOME_INSERT = `INSERT INTO outcome
|
|
5
|
+
(root_session_id, task_id, commit_sha, kind, ts)
|
|
6
|
+
VALUES (?,?,?,?,?)`;
|
|
7
|
+
/**
|
|
8
|
+
* Record one outcome.
|
|
9
|
+
*
|
|
10
|
+
* The kind is checked against the four of §6.1 before the row is written, so
|
|
11
|
+
* a mistake is a message and not a constraint violation. The write is
|
|
12
|
+
* idempotent: an outcome that is already there — the same kind, the same
|
|
13
|
+
* task and the same commit — is not written twice, because the scanners are
|
|
14
|
+
* commands that run over the same history again and again.
|
|
15
|
+
*
|
|
16
|
+
* Returns whether a row was written.
|
|
17
|
+
*/
|
|
18
|
+
export function recordOutcome(db, outcome) {
|
|
19
|
+
if (!OUTCOME_KINDS.includes(outcome.kind)) {
|
|
20
|
+
throw new Error(`unknown outcome kind: ${outcome.kind}`);
|
|
21
|
+
}
|
|
22
|
+
if (outcomeExists(db, outcome))
|
|
23
|
+
return false;
|
|
24
|
+
const ts = outcome.ts ?? new Date().toISOString();
|
|
25
|
+
withBusyRetry(() => {
|
|
26
|
+
db.query(OUTCOME_INSERT).run(outcome.rootSessionId ?? null, outcome.taskId ?? null, outcome.commitSha ?? null, outcome.kind, ts);
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** Whether the ledger already holds this outcome. */
|
|
31
|
+
function outcomeExists(db, outcome) {
|
|
32
|
+
const found = db
|
|
33
|
+
.query(`SELECT 1 FROM outcome
|
|
34
|
+
WHERE kind = ? AND task_id IS ? AND commit_sha IS ?
|
|
35
|
+
LIMIT 1`)
|
|
36
|
+
.get(outcome.kind, outcome.taskId ?? null, outcome.commitSha ?? null);
|
|
37
|
+
return found !== null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The outcomes of the commits that carry a `Refs:` trailer (§6.5).
|
|
41
|
+
*
|
|
42
|
+
* The trailer is what the pre-commit hook requires of an agent commit, so
|
|
43
|
+
* the commits of the agents are the ones this finds. A commit without a
|
|
44
|
+
* trailer names no task and becomes no outcome.
|
|
45
|
+
*/
|
|
46
|
+
export function scanRefsTrailers(gitDir, since) {
|
|
47
|
+
const candidates = [];
|
|
48
|
+
for (const commit of gitCommits(gitDir, since)) {
|
|
49
|
+
for (const taskId of refsTrailers(commit.message)) {
|
|
50
|
+
candidates.push({
|
|
51
|
+
kind: 'commit',
|
|
52
|
+
taskId,
|
|
53
|
+
commitSha: commit.sha,
|
|
54
|
+
ts: new Date(commit.at * 1000).toISOString(),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return candidates;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The outcomes of the task transitions (§6.5).
|
|
62
|
+
*
|
|
63
|
+
* A task that reached `completed` or `scrapped` is an outcome. The moment of
|
|
64
|
+
* the transition is the moment the task last changed; a task that carries no
|
|
65
|
+
* timestamp is taken as it is now, because the store says its status and
|
|
66
|
+
* the time of the change is not in it.
|
|
67
|
+
*/
|
|
68
|
+
export async function scanTaskTransitions(root, since, reader) {
|
|
69
|
+
const tasks = await reader(root);
|
|
70
|
+
const candidates = [];
|
|
71
|
+
for (const task of tasks) {
|
|
72
|
+
if (task.status !== 'completed' && task.status !== 'scrapped')
|
|
73
|
+
continue;
|
|
74
|
+
const ts = task.updated_at ?? new Date().toISOString();
|
|
75
|
+
if (since.length > 0 && ts < since)
|
|
76
|
+
continue;
|
|
77
|
+
candidates.push({
|
|
78
|
+
kind: task.status === 'completed' ? 'task_completed' : 'task_scrapped',
|
|
79
|
+
taskId: task.id,
|
|
80
|
+
ts,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return candidates;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=outcomes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.js","sourceRoot":"","sources":["../src/outcomes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.test.d.ts","sourceRoot":"","sources":["../src/outcomes.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { closeLedger, openLedger } from './db.js';
|
|
6
|
+
import { runGit } from './git.js';
|
|
7
|
+
import { recordAssistantMessage } from './ingest.js';
|
|
8
|
+
import { recordOutcome, scanRefsTrailers, scanTaskTransitions } from './outcomes.js';
|
|
9
|
+
import { tokensPerCommit, tokensPerCompletedTask, tokensPerResult } from './query.js';
|
|
10
|
+
let root;
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
root = await mkdtemp(join(tmpdir(), 'openagentsy-outcomes-'));
|
|
13
|
+
});
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
await rm(root, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
describe('the outcome recorder', () => {
|
|
18
|
+
it('writes the outcome once', () => {
|
|
19
|
+
const db = openLedger(root);
|
|
20
|
+
try {
|
|
21
|
+
expect(recordOutcome(db, { kind: 'task_completed', taskId: 'task-1' })).toBe(true);
|
|
22
|
+
expect(recordOutcome(db, { kind: 'task_completed', taskId: 'task-1' })).toBe(false);
|
|
23
|
+
const rows = db.query('SELECT COUNT(*) AS count FROM outcome').get();
|
|
24
|
+
expect(rows.count).toBe(1);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
closeLedger(db);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it('refuses a kind the schema does not know', () => {
|
|
31
|
+
const db = openLedger(root);
|
|
32
|
+
try {
|
|
33
|
+
expect(() => recordOutcome(db, { kind: 'nonsense' })).toThrow(/unknown outcome kind/);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
closeLedger(db);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
it('keeps the task and the commit of an outcome', () => {
|
|
40
|
+
const db = openLedger(root);
|
|
41
|
+
try {
|
|
42
|
+
recordOutcome(db, {
|
|
43
|
+
kind: 'commit',
|
|
44
|
+
taskId: 'task-1',
|
|
45
|
+
commitSha: 'abc1234',
|
|
46
|
+
rootSessionId: 'ses_root',
|
|
47
|
+
});
|
|
48
|
+
const row = db
|
|
49
|
+
.query('SELECT kind, task_id, commit_sha, root_session_id FROM outcome')
|
|
50
|
+
.get();
|
|
51
|
+
expect(row.kind).toBe('commit');
|
|
52
|
+
expect(row.task_id).toBe('task-1');
|
|
53
|
+
expect(row.commit_sha).toBe('abc1234');
|
|
54
|
+
expect(row.root_session_id).toBe('ses_root');
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
closeLedger(db);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('the refs trailer scanner', () => {
|
|
62
|
+
it('finds the commits that name a task', async () => {
|
|
63
|
+
const repo = await mkdtemp(join(tmpdir(), 'openagentsy-repo-'));
|
|
64
|
+
await seedRepo(repo);
|
|
65
|
+
const outcomes = scanRefsTrailers(repo, '30 days ago');
|
|
66
|
+
expect(outcomes).toHaveLength(1);
|
|
67
|
+
expect(outcomes[0]?.kind).toBe('commit');
|
|
68
|
+
expect(outcomes[0]?.taskId).toBe('task-1');
|
|
69
|
+
expect(outcomes[0]?.commitSha).toMatch(/^[0-9a-f]{40}$/);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('the task transition scanner', () => {
|
|
73
|
+
it('turns the finished tasks into outcomes', async () => {
|
|
74
|
+
const outcomes = await scanTaskTransitions('/beans', '2026-01-01T00:00:00.000Z', async () => [
|
|
75
|
+
{ id: 'task-1', status: 'completed', updated_at: '2026-01-02T00:00:00.000Z' },
|
|
76
|
+
{ id: 'task-2', status: 'scrapped', updated_at: '2026-01-02T00:00:00.000Z' },
|
|
77
|
+
{ id: 'task-3', status: 'in-progress', updated_at: '2026-01-02T00:00:00.000Z' },
|
|
78
|
+
]);
|
|
79
|
+
expect(outcomes).toHaveLength(2);
|
|
80
|
+
expect(outcomes.find((outcome) => outcome.taskId === 'task-1')?.kind).toBe('task_completed');
|
|
81
|
+
expect(outcomes.find((outcome) => outcome.taskId === 'task-2')?.kind).toBe('task_scrapped');
|
|
82
|
+
});
|
|
83
|
+
it('leaves the tasks that finished before the window alone', async () => {
|
|
84
|
+
const outcomes = await scanTaskTransitions('/beans', '2026-02-01T00:00:00.000Z', async () => [
|
|
85
|
+
{ id: 'task-1', status: 'completed', updated_at: '2026-01-02T00:00:00.000Z' },
|
|
86
|
+
]);
|
|
87
|
+
expect(outcomes).toHaveLength(0);
|
|
88
|
+
});
|
|
89
|
+
it('reads the store through the reader it is given', async () => {
|
|
90
|
+
const seen = [];
|
|
91
|
+
await scanTaskTransitions('/beans', '2026-01-01T00:00:00.000Z', async (root) => {
|
|
92
|
+
seen.push(root);
|
|
93
|
+
return [];
|
|
94
|
+
});
|
|
95
|
+
expect(seen).toEqual(['/beans']);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('the outcome joins', () => {
|
|
99
|
+
it('counts the tokens of a completed task', () => {
|
|
100
|
+
const db = openLedger(root);
|
|
101
|
+
try {
|
|
102
|
+
recordAssistantMessage(db, {
|
|
103
|
+
id: 'm1',
|
|
104
|
+
sessionID: 'ses_1',
|
|
105
|
+
providerID: 'anthropic',
|
|
106
|
+
modelID: 'claude-sonnet-4-5',
|
|
107
|
+
tokens: { input: 100, output: 40, reasoning: 10, cache: { write: 5 } },
|
|
108
|
+
}, {
|
|
109
|
+
rootSessionId: 'ses_1',
|
|
110
|
+
role: 'builder',
|
|
111
|
+
activeTaskId: 'task-1',
|
|
112
|
+
ts: '2026-01-02T00:00:00.000Z',
|
|
113
|
+
});
|
|
114
|
+
recordOutcome(db, { kind: 'task_completed', taskId: 'task-1' });
|
|
115
|
+
const rows = tokensPerCompletedTask(db);
|
|
116
|
+
expect(rows).toHaveLength(1);
|
|
117
|
+
expect(rows[0]?.taskId).toBe('task-1');
|
|
118
|
+
expect(rows[0]?.tokens).toBe(155);
|
|
119
|
+
recordOutcome(db, { kind: 'commit', taskId: 'task-1', commitSha: 'abc1234' });
|
|
120
|
+
const commits = tokensPerCommit(db);
|
|
121
|
+
expect(commits).toHaveLength(1);
|
|
122
|
+
expect(commits[0]?.commitSha).toBe('abc1234');
|
|
123
|
+
expect(commits[0]?.tokens).toBe(155);
|
|
124
|
+
const result = tokensPerResult(db);
|
|
125
|
+
expect(result.tasksCompleted).toBe(1);
|
|
126
|
+
expect(result.tokensPerTask).toBe(155);
|
|
127
|
+
expect(result.commits).toBe(1);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
closeLedger(db);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
/** A repository with one commit that names a task and one that does not. */
|
|
135
|
+
async function seedRepo(repo) {
|
|
136
|
+
const git = (...args) => {
|
|
137
|
+
const result = runGit(repo, [
|
|
138
|
+
'-c',
|
|
139
|
+
'user.email=test@example.com',
|
|
140
|
+
'-c',
|
|
141
|
+
'user.name=Test',
|
|
142
|
+
'-c',
|
|
143
|
+
'commit.gpgsign=false',
|
|
144
|
+
...args,
|
|
145
|
+
]);
|
|
146
|
+
if (!result.ok)
|
|
147
|
+
throw new Error(result.stderr);
|
|
148
|
+
};
|
|
149
|
+
git('init', '-q');
|
|
150
|
+
git('config', 'user.email', 'test@example.com');
|
|
151
|
+
git('config', 'user.name', 'Test');
|
|
152
|
+
git('config', 'commit.gpgsign', 'false');
|
|
153
|
+
await writeFile(join(repo, 'a.txt'), 'one\n');
|
|
154
|
+
git('add', 'a.txt');
|
|
155
|
+
git('commit', '-q', '-m', 'feat: add\n\nRefs: task-1');
|
|
156
|
+
await writeFile(join(repo, 'b.txt'), 'two\n');
|
|
157
|
+
git('add', 'b.txt');
|
|
158
|
+
git('commit', '-q', '-m', 'chore: add');
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=outcomes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.test.js","sourceRoot":"","sources":["../src/outcomes.test.ts"],"names":[],"mappings":"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"}
|