@open-tomato/rafa 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -23
- package/dist/PROMPT.md +18 -1
- package/dist/adapters/logger/settings.d.ts +132 -0
- package/dist/bugs/closest.d.ts +19 -0
- package/dist/bugs/fenced.d.ts +8 -0
- package/dist/bugs/places.d.ts +20 -0
- package/dist/bugs/record.d.ts +145 -0
- package/dist/bugs/title.d.ts +83 -0
- package/dist/bundled/agents/doc-updater.md +17 -3
- package/dist/bundled/agents/loop-implementer.md +14 -2
- package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +10 -3
- package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +5 -0
- package/dist/bundled/skills/dev-planner/SKILL.md +101 -18
- package/dist/cli.js +74726 -62960
- package/dist/config/entry.d.ts +119 -0
- package/dist/config/index.d.ts +36 -0
- package/dist/config/index.js +51 -0
- package/dist/config/shape.d.ts +232 -0
- package/dist/config-registry.d.ts +127 -0
- package/dist/config-schema-board-project.d.ts +157 -0
- package/dist/config-schema-bugs.d.ts +71 -0
- package/dist/config-schema-errors.d.ts +54 -0
- package/dist/config-schema-features.d.ts +64 -0
- package/dist/config-schema-hub.d.ts +150 -0
- package/dist/config-schema-issues.d.ts +95 -0
- package/dist/config-schema-logger.d.ts +91 -0
- package/dist/config-schema-loop-continue.d.ts +122 -0
- package/dist/config-schema-loop-retries.d.ts +94 -0
- package/dist/config-schema-release.d.ts +307 -0
- package/dist/config-schema-tests.d.ts +170 -0
- package/dist/config-schema-triage.d.ts +142 -0
- package/dist/config-schema-wrap-up.d.ts +96 -0
- package/dist/core/command/command.d.ts +228 -0
- package/dist/core/command/confirm.d.ts +67 -0
- package/dist/core/command/index.d.ts +112 -0
- package/dist/core/command/lazy.d.ts +54 -0
- package/dist/core/command/registry-types.d.ts +96 -0
- package/dist/core/command/running.d.ts +96 -0
- package/dist/core/command/spends.d.ts +39 -0
- package/dist/core/command/types.d.ts +92 -0
- package/dist/core/command/version.d.ts +4 -0
- package/dist/core/config/branch-template.d.ts +20 -0
- package/dist/core/config/config-items.d.ts +86 -0
- package/dist/core/config/config-load.d.ts +47 -0
- package/dist/core/config/config-origins.d.ts +57 -0
- package/dist/core/config/config-readers.d.ts +167 -0
- package/dist/core/config/config-schema.d.ts +207 -0
- package/dist/core/config/config-sections.d.ts +493 -0
- package/dist/core/config/config.d.ts +320 -0
- package/dist/core/config/core-section.d.ts +39 -0
- package/dist/core/config/dangerous.d.ts +58 -0
- package/dist/core/config/default-routing.d.ts +16 -0
- package/dist/core/config/index.d.ts +141 -0
- package/dist/core/config/scope.d.ts +42 -0
- package/dist/core/config/section.d.ts +206 -0
- package/dist/core/config/sections.d.ts +45 -0
- package/dist/core/config/standard-schema.d.ts +27 -0
- package/dist/core/git/commit.d.ts +232 -0
- package/dist/core/git/gh-runner.d.ts +24 -0
- package/dist/core/git/git.d.ts +22 -0
- package/dist/core/git/index.d.ts +85 -0
- package/dist/core/loop/claude.d.ts +388 -0
- package/dist/core/loop/hop-record.d.ts +52 -0
- package/dist/core/loop/index.d.ts +179 -0
- package/dist/core/loop/session-env.d.ts +20 -0
- package/dist/core/loop/session-record-parse.d.ts +63 -0
- package/dist/core/loop/sessions.d.ts +408 -0
- package/dist/core/loop/suite-failure.d.ts +25 -0
- package/dist/core/output/active.d.ts +100 -0
- package/dist/core/output/stream.d.ts +20 -0
- package/dist/core/output/text.d.ts +55 -0
- package/dist/core/plan/block-reader.d.ts +46 -0
- package/dist/core/plan/blocks.d.ts +139 -0
- package/dist/core/plan/declaration.d.ts +366 -0
- package/dist/core/plan/plan-stamp.d.ts +75 -0
- package/dist/core/runtime/identity.d.ts +21 -0
- package/dist/core/runtime/index.d.ts +46 -0
- package/dist/effort/store/bring-forward.d.ts +131 -0
- package/dist/effort/store/check-rows.d.ts +31 -0
- package/dist/effort/store/commit-stats.d.ts +42 -0
- package/dist/effort/store/copy.d.ts +35 -0
- package/dist/effort/store/development-build.d.ts +105 -0
- package/dist/effort/store/effort-levels.d.ts +13 -0
- package/dist/effort/store/findings.d.ts +214 -0
- package/dist/effort/store/fix-schema.d.ts +184 -0
- package/dist/effort/store/index.js +10 -7
- package/dist/effort/store/location.d.ts +44 -0
- package/dist/effort/store/merge-commit-gaps.d.ts +68 -0
- package/dist/effort/store/merge-conflicts.d.ts +91 -0
- package/dist/effort/store/merge-rules.d.ts +82 -0
- package/dist/effort/store/merge-store.d.ts +211 -0
- package/dist/effort/store/merge-union.d.ts +92 -0
- package/dist/effort/store/migrate.d.ts +146 -0
- package/dist/effort/store/migrations.d.ts +92 -0
- package/dist/effort/store/ndjson-files.d.ts +114 -0
- package/dist/effort/store/origins.d.ts +116 -0
- package/dist/effort/store/plan-stubs.d.ts +79 -0
- package/dist/effort/store/rebuild-aside.d.ts +172 -0
- package/dist/effort/store/schema-plan.d.ts +102 -0
- package/dist/effort/store/session-row-types.d.ts +64 -0
- package/dist/effort/store/settings.d.ts +13 -0
- package/dist/effort/store/sqlite.d.ts +340 -0
- package/dist/effort/store/store-generation.d.ts +17 -0
- package/dist/effort/store/store-identity.d.ts +220 -0
- package/dist/effort/store/store-meta.d.ts +305 -0
- package/dist/effort/store/store-replaced.d.ts +72 -0
- package/dist/effort/store/types.d.ts +223 -0
- package/dist/errors/codes.d.ts +56 -0
- package/dist/errors/rafa-codes.d.ts +36 -0
- package/dist/index-2a7nb3an.js +125 -0
- package/dist/{index-r28fzsvc.js → index-39kbcw68.js} +350 -165
- package/dist/{index-nv34ptht.js → index-77ajfffn.js} +14 -9
- package/dist/index-cy98y87p.js +2597 -0
- package/dist/index-gqz4jn6j.js +13447 -0
- package/dist/index-gvek4r8m.js +710 -0
- package/dist/index-j4k2vdz3.js +1125 -0
- package/dist/index-jh1r4kcn.js +1067 -0
- package/dist/{index-mgh2s9bj.js → index-ykggr043.js} +58 -455
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index.js +14715 -21516
- package/dist/learning/index.js +5 -6
- package/dist/plan/index.js +12 -11
- package/dist/plan-prompt.md +3 -1
- package/dist/ports/index.d.ts +732 -0
- package/dist/ports/learning.d.ts +101 -0
- package/dist/ports/log-level.d.ts +13 -0
- package/dist/ports/spec-review.d.ts +66 -0
- package/dist/pr/checks.d.ts +133 -0
- package/dist/pr/types.d.ts +422 -0
- package/dist/ready-4f8edezt.js +2016 -0
- package/dist/release-templates/release-bot-fragment.yml +153 -0
- package/dist/release-templates/release-publish.yml +110 -0
- package/dist/release-templates/release-settle.yml +122 -0
- package/dist/release-templates/release-tag.yml +102 -0
- package/dist/report/parse.d.ts +415 -0
- package/dist/schema/project-id.d.ts +79 -0
- package/dist/schema/tiers.d.ts +107 -0
- package/package.json +41 -3
- package/dist/index-hf2k497d.js +0 -16
- package/dist/index-nsr78ct2.js +0 -8219
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store directory, relative to the repo root.
|
|
3
|
+
*
|
|
4
|
+
* Spelled once on purpose — see the module note. `join` rather than a
|
|
5
|
+
* literal so the separator is the platform's, which keeps a path built
|
|
6
|
+
* here comparable to one a caller built with `join` of its own.
|
|
7
|
+
*/
|
|
8
|
+
export declare const EFFORT_STORE_DIR: string;
|
|
9
|
+
/** The row kinds the collector writes, one store file each. */
|
|
10
|
+
export type EffortRowKind = 'sessions' | 'commits';
|
|
11
|
+
/** What one read of a store file found. */
|
|
12
|
+
export interface StoreReadResult<T> {
|
|
13
|
+
/** Every plain-object line, in the order it was appended. */
|
|
14
|
+
rows: T[];
|
|
15
|
+
/** Lines the file held, excluding the trailing newline's empty tail. */
|
|
16
|
+
lineCount: number;
|
|
17
|
+
/** Lines that produced no row: blank, malformed, or not an object. */
|
|
18
|
+
unparsedLineCount: number;
|
|
19
|
+
}
|
|
20
|
+
/** What one append did. */
|
|
21
|
+
export interface AppendResult {
|
|
22
|
+
/** The file written, whether or not anything was written to it. */
|
|
23
|
+
path: string;
|
|
24
|
+
/** Rows appended. Zero writes nothing and creates no file. */
|
|
25
|
+
appended: number;
|
|
26
|
+
/** Rows whose key was already held, on disk or earlier in the batch. */
|
|
27
|
+
skipped: number;
|
|
28
|
+
/**
|
|
29
|
+
* True when the file did not end in a newline and this append wrote
|
|
30
|
+
* one before its first row. The partial line is left in place and
|
|
31
|
+
* reads as unparsed forever, which is what makes the row it half
|
|
32
|
+
* held get collected again.
|
|
33
|
+
*/
|
|
34
|
+
separatedPartialLine: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** A store bound to one file and one key projection. */
|
|
37
|
+
export interface EffortStore<T> {
|
|
38
|
+
/** The file this store reads and appends to. */
|
|
39
|
+
readonly path: string;
|
|
40
|
+
/** Every row currently on disk, with the line arithmetic. */
|
|
41
|
+
readRows: () => StoreReadResult<T>;
|
|
42
|
+
/** The keys already held — what a collector skips. */
|
|
43
|
+
collectedKeys: () => Set<string>;
|
|
44
|
+
/** Appends the rows this store does not already hold. */
|
|
45
|
+
append: (rows: readonly T[]) => AppendResult;
|
|
46
|
+
}
|
|
47
|
+
/** The name of one row kind's store file, without its directory. */
|
|
48
|
+
export declare function effortStoreFileName(kind: EffortRowKind): string;
|
|
49
|
+
/**
|
|
50
|
+
* Resolves one store file under a repo root, always under
|
|
51
|
+
* {@link EFFORT_STORE_DIR}. The NDJSON backend resolves its files
|
|
52
|
+
* through `store/location.ts` instead, which `RAFA_EFFORT_DIR` can move.
|
|
53
|
+
*/
|
|
54
|
+
export declare function effortStorePath(repoRoot: string, kind: EffortRowKind): string;
|
|
55
|
+
/**
|
|
56
|
+
* Parses store text into rows.
|
|
57
|
+
*
|
|
58
|
+
* The trailing empty element a final newline produces is dropped —
|
|
59
|
+
* exactly one, so a genuinely blank last line is still counted.
|
|
60
|
+
* Everything else that yields no row is counted as unparsed, which is
|
|
61
|
+
* what keeps `rows + unparsed === lineCount` exact and makes a
|
|
62
|
+
* truncated final line visible instead of silent.
|
|
63
|
+
*
|
|
64
|
+
* Splitting on `/\r?\n/` rather than the bare newline is currently
|
|
65
|
+
* UNOBSERVABLE and kept anyway. Measured: the bare split leaves a
|
|
66
|
+
* carriage return on the end of every line and every case still
|
|
67
|
+
* passes, because `JSON.parse` tolerates trailing whitespace and a
|
|
68
|
+
* lone `\r` trims to empty. That equivalence is two downstream
|
|
69
|
+
* tolerances rather than a property of this function, and the next
|
|
70
|
+
* check added here — a length test, a prefix test — would silently
|
|
71
|
+
* depend on neither having changed. Taking the carriage return off at
|
|
72
|
+
* the split is what keeps that from mattering.
|
|
73
|
+
*
|
|
74
|
+
* The `T` is the caller's assertion. The only structural check made
|
|
75
|
+
* here is that the line parsed to a plain object.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseStoreText<T>(text: string | null): StoreReadResult<T>;
|
|
78
|
+
/** Reads every row a store file holds, with its line arithmetic. */
|
|
79
|
+
export declare function readStoreRows<T>(path: string): StoreReadResult<T>;
|
|
80
|
+
/**
|
|
81
|
+
* Projects rows onto their keys.
|
|
82
|
+
*
|
|
83
|
+
* A row whose key is absent or empty contributes nothing rather than
|
|
84
|
+
* seeding the set with a placeholder every other keyless row would
|
|
85
|
+
* then collide with. {@link appendRows} refuses to write one in the
|
|
86
|
+
* first place; this tolerates the ones a hand-edited store might hold.
|
|
87
|
+
*/
|
|
88
|
+
export declare function keysOfRows<T>(rows: readonly T[], keyOf: (row: T) => string | null | undefined): Set<string>;
|
|
89
|
+
/** The keys a store file already holds — what a collector skips. */
|
|
90
|
+
export declare function collectedKeys<T>(path: string, keyOf: (row: T) => string | null | undefined): Set<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Appends the rows a store file does not already hold.
|
|
93
|
+
*
|
|
94
|
+
* The batch is key-validated before anything is opened, so a keyless
|
|
95
|
+
* row leaves the file untouched rather than half-appended. Rows are
|
|
96
|
+
* serialised one per line: `JSON.stringify` escapes a newline inside
|
|
97
|
+
* a string value, so no row content can ever split a line in two.
|
|
98
|
+
*
|
|
99
|
+
* Writing nothing writes NOTHING — no file, no directory, no trailing
|
|
100
|
+
* newline. An empty first collect leaves no trace, which is the only
|
|
101
|
+
* answer consistent with a store whose absence means "empty".
|
|
102
|
+
*/
|
|
103
|
+
export declare function appendRows<T>(path: string, rows: readonly T[], keyOf: (row: T) => string | null | undefined): AppendResult;
|
|
104
|
+
/**
|
|
105
|
+
* Binds a store file to the key projection it is deduplicated by.
|
|
106
|
+
*
|
|
107
|
+
* The binding is the point rather than the ergonomics. Incrementality
|
|
108
|
+
* fails silently when the set a collector SKIPS by and the set an
|
|
109
|
+
* append DEDUPES by are built from two different projections of a
|
|
110
|
+
* row — the collector re-reads work it already holds, or the store
|
|
111
|
+
* grows a duplicate per run, and both look like a store that is
|
|
112
|
+
* simply working. One `keyOf` per store makes that unrepresentable.
|
|
113
|
+
*/
|
|
114
|
+
export declare function openEffortStore<T>(path: string, keyOf: (row: T) => string | null | undefined): EffortStore<T>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The origin pair a production insert stamps on a new row it writes:
|
|
3
|
+
* `origin_store`, the store id `store_meta` records, and `origin_seq`,
|
|
4
|
+
* the row's own `seq`. An insert that carries a row over from another
|
|
5
|
+
* store or file keeps the pair that row holds; {@link ORIGIN_TABLES}
|
|
6
|
+
* names the three.
|
|
7
|
+
*
|
|
8
|
+
* ## One counter, not two
|
|
9
|
+
*
|
|
10
|
+
* `origin_seq` is the row's local `seq` at insert, never a second
|
|
11
|
+
* counter. So a store that mints a new origin, being a copy, keeps
|
|
12
|
+
* counting `seq` from the rows it holds, and its first row under the
|
|
13
|
+
* new origin carries the next `seq` rather than 1. Two stores that
|
|
14
|
+
* share rows then disagree on `origin_store` for everything written
|
|
15
|
+
* after the copy, which is what keeps their pairs apart.
|
|
16
|
+
*
|
|
17
|
+
* Every stamped insert names `seq` itself, as `COALESCE(MAX(seq), 0) + 1`
|
|
18
|
+
* over its own table, and writes that same value into `origin_seq`, so
|
|
19
|
+
* the two cannot differ. SQLite would pick the same rowid for an
|
|
20
|
+
* `INTEGER PRIMARY KEY` left out, but only while the largest rowid is
|
|
21
|
+
* below 2^63 - 1: measured with Bun 1.4.2, a row inserted after one
|
|
22
|
+
* at `9223372036854775807` got `4005337119280845049`, a random pick
|
|
23
|
+
* no `MAX(seq)` read beforehand predicts. Both subqueries sit in the one
|
|
24
|
+
* `INSERT` statement, which SQLite runs atomically under the write lock
|
|
25
|
+
* it takes, so a row inserted by another process cannot land between
|
|
26
|
+
* the reading of `MAX(seq)` and the insert. Nothing here reads a row
|
|
27
|
+
* back or updates one after the insert.
|
|
28
|
+
*
|
|
29
|
+
* ## Where the origin comes from
|
|
30
|
+
*
|
|
31
|
+
* The insert reads `store_id` from `store_meta` (`id = 1`) in the same
|
|
32
|
+
* statement. `withSqliteStore` settles the store's identity before any
|
|
33
|
+
* writer runs (`store-meta.ts`), so on a `write` open the row holds the
|
|
34
|
+
* origin this very file was minted for: the one recorded, kept, or the
|
|
35
|
+
* one just minted for a copy. A store with no `store_meta` row, as one
|
|
36
|
+
* under `tmpdir()` outside any repository with commits is left, stamps
|
|
37
|
+
* NULL in both columns, as a runtime before `row-origins` does. It
|
|
38
|
+
* never stamps a `seq` without a store: a pair is both or neither.
|
|
39
|
+
*
|
|
40
|
+
* ## No trigger, and no `UPDATE`
|
|
41
|
+
*
|
|
42
|
+
* A trigger filling the columns would break `writers` as
|
|
43
|
+
* `classifyMigration` reads it, since an older runtime's insert would
|
|
44
|
+
* fire it. A second statement updating the pair after the insert would
|
|
45
|
+
* be a production `UPDATE … SET origin_store` that the merge's registry
|
|
46
|
+
* would have to give a rule. The pair is therefore written by the
|
|
47
|
+
* insert's own column list, which {@link STAMPED_COLUMNS} and
|
|
48
|
+
* {@link stampedValues} spell for every writer alike.
|
|
49
|
+
*
|
|
50
|
+
* ## Readers
|
|
51
|
+
*
|
|
52
|
+
* No reader filters on either column, so a row an older runtime
|
|
53
|
+
* inserts, NULL in both, is read as every stamped row is. The partial
|
|
54
|
+
* unique index `<table>_by_origin` covers only rows whose
|
|
55
|
+
* `origin_store` is not NULL, so any number of such rows coexist.
|
|
56
|
+
*
|
|
57
|
+
* A reader that answers rows in order and may run over a merged store
|
|
58
|
+
* sorts by {@link ACROSS_STORES_ORDER}, which names both columns. `seq`
|
|
59
|
+
* alone cannot be that order: a merge inserts the other store's rows
|
|
60
|
+
* under new local `seq` values after its own, so the two sides of one
|
|
61
|
+
* merge would read the same rows in two orders.
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* The tables a merge unions, each carrying `origin_store` and
|
|
65
|
+
* `origin_seq`: since migration `row-origins` for the twelve tables
|
|
66
|
+
* older than it, and from the migration that creates it for
|
|
67
|
+
* `stale_docs` and `bug_routes`. A production insert that writes a new
|
|
68
|
+
* row into one of them is stamped. Three inserts carry rows that exist
|
|
69
|
+
* already and leave the pair as the row holds it:
|
|
70
|
+
* the copy in `fix-schema.ts`, column for column from the live store;
|
|
71
|
+
* the union in `merge-union.ts`, which gives another store's row a new
|
|
72
|
+
* local `seq` and keeps its origin; and the restore in
|
|
73
|
+
* `fixture-extract.ts`, which builds a store from an extract's rows.
|
|
74
|
+
* `origins.test.ts` reads every insert from source and spells both
|
|
75
|
+
* lists. `schema_migrations`, `store_meta`, `store_replaced`, `merges`
|
|
76
|
+
* and `merge_conflicts` are the store's own bookkeeping and carry no
|
|
77
|
+
* origin.
|
|
78
|
+
*/
|
|
79
|
+
export declare const ORIGIN_TABLES: readonly ["sessions", "commits", "findings", "blockers", "out_of_scope_bugs", "report_absences", "task_reports", "preflight", "dispatches", "changes", "skill_invocations", "plan_ci", "stale_docs", "bug_routes"];
|
|
80
|
+
/** One of the {@link ORIGIN_TABLES}. */
|
|
81
|
+
export type OriginTable = typeof ORIGIN_TABLES[number];
|
|
82
|
+
/**
|
|
83
|
+
* The `ORDER BY` terms of a reader that answers the rows of a table
|
|
84
|
+
* with `collected_at` in the same order on every store holding them:
|
|
85
|
+
* the write's time first, then the origin pair, then the local `seq`.
|
|
86
|
+
*
|
|
87
|
+
* - `collected_at` orders rows written at different times, whichever
|
|
88
|
+
* store wrote them and whichever side ran a merge.
|
|
89
|
+
* - The origin pair breaks a tie. One write stamps every row it
|
|
90
|
+
* inserts with one time, and `origin_seq` is the `seq` each row got
|
|
91
|
+
* in the store that wrote it, so one device's rows of one write keep
|
|
92
|
+
* the order that device appended them in, on any store, and two
|
|
93
|
+
* devices' rows at one time are ordered by their `origin_store`.
|
|
94
|
+
* - `seq` breaks what remains: rows with a NULL origin, which SQLite
|
|
95
|
+
* sorts before every origin. Among those at one time the order is
|
|
96
|
+
* the store's own append order, which is not the same on both sides
|
|
97
|
+
* of a merge.
|
|
98
|
+
*
|
|
99
|
+
* A clock that goes backwards reorders rows here; the append order no
|
|
100
|
+
* longer wins over the stamp.
|
|
101
|
+
*/
|
|
102
|
+
export declare const ACROSS_STORES_ORDER = "collected_at, origin_store, origin_seq, seq";
|
|
103
|
+
/**
|
|
104
|
+
* The columns a stamped insert names after its own, in the order
|
|
105
|
+
* {@link stampedValues} answers their values.
|
|
106
|
+
*/
|
|
107
|
+
export declare const STAMPED_COLUMNS = "seq, origin_store, origin_seq";
|
|
108
|
+
/**
|
|
109
|
+
* The SQL values of {@link STAMPED_COLUMNS} for an insert into `table`:
|
|
110
|
+
* the next `seq`, the store's origin, and that `seq` again as
|
|
111
|
+
* `origin_seq` when the store has an origin, NULL beside a NULL origin.
|
|
112
|
+
*
|
|
113
|
+
* `table` is interpolated into the SQL, which is safe because the type
|
|
114
|
+
* admits only the names in {@link ORIGIN_TABLES}.
|
|
115
|
+
*/
|
|
116
|
+
export declare function stampedValues(table: OriginTable): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch and plan-stub resolution: how a branch name splits into a type
|
|
3
|
+
* and a stub, how plan stubs are read out of a directory listing, and
|
|
4
|
+
* how a branch stub resolves against them.
|
|
5
|
+
*
|
|
6
|
+
* Moved down from `src/effort/attribution.ts`, which attributes a
|
|
7
|
+
* session to its plan, issue and task text and re-exports these names
|
|
8
|
+
* under the same names: the store's row types (`types.ts`) and its plan
|
|
9
|
+
* CI reader (`plan-ci.ts`) need the resolution but not the session
|
|
10
|
+
* attribution, so they import it here and the store imports nothing
|
|
11
|
+
* above core for it. The module note of `attribution.ts` still explains
|
|
12
|
+
* why a branch stub is not a plan stub and why the queue id is matched
|
|
13
|
+
* whole.
|
|
14
|
+
*/
|
|
15
|
+
/** One branch name split into the two parts attribution reads. */
|
|
16
|
+
export interface BranchAttribution {
|
|
17
|
+
/** The branch as recorded, trimmed, or null when there was none. */
|
|
18
|
+
branch: string | null;
|
|
19
|
+
/** The segment before the first slash, or null when there is none. */
|
|
20
|
+
type: string | null;
|
|
21
|
+
/** The segment after it, or null when the branch carries no stub. */
|
|
22
|
+
stub: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* How a session was matched to a plan.
|
|
26
|
+
*
|
|
27
|
+
* `stamped` is the only member that does not come from a branch name:
|
|
28
|
+
* the loop writes the stub into every prompt it dispatches, so it is
|
|
29
|
+
* read back directly and outranks the branch. The rest describe the
|
|
30
|
+
* branch-stub path, which is the fallback for a session recorded
|
|
31
|
+
* before stamping existed, or one the loop did not dispatch.
|
|
32
|
+
*/
|
|
33
|
+
export type PlanStubMatch = 'stamped' | 'exact' | 'queue-id' | 'none' | 'ambiguous';
|
|
34
|
+
/** The plan a branch stub resolved to, and how it got there. */
|
|
35
|
+
export interface PlanStubResolution {
|
|
36
|
+
/** The resolved plan stub, or null unless the match is decisive. */
|
|
37
|
+
stub: string | null;
|
|
38
|
+
match: PlanStubMatch;
|
|
39
|
+
/**
|
|
40
|
+
* Every plan stub the queue id reached. One member on a `queue-id`
|
|
41
|
+
* match, the branch stub itself on an `exact` one, and the whole
|
|
42
|
+
* contested set on an `ambiguous` one — which is what makes an
|
|
43
|
+
* ambiguity reportable rather than merely refused.
|
|
44
|
+
*/
|
|
45
|
+
candidates: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Splits a branch name into its type prefix and its plan stub.
|
|
49
|
+
*
|
|
50
|
+
* A name with no slash has no stub — that is the `main` and `pw-align`
|
|
51
|
+
* case and it is the expected answer, not a degraded one. A stub that
|
|
52
|
+
* would itself contain a slash is refused for a narrower reason: no
|
|
53
|
+
* plan file can be named after it, `plan.ts` writing
|
|
54
|
+
* `PLAN-<stub>.md` into one flat directory.
|
|
55
|
+
*/
|
|
56
|
+
export declare function attributeBranch(branch: string | null | undefined): BranchAttribution;
|
|
57
|
+
/**
|
|
58
|
+
* Reads plan stubs out of a directory listing.
|
|
59
|
+
*
|
|
60
|
+
* Takes names rather than a directory so the naming convention lives in
|
|
61
|
+
* one place while the read stays with the caller — the plans directory,
|
|
62
|
+
* `plan.dir`, is untracked by default and can be absent entirely, which
|
|
63
|
+
* is the caller's problem to report and not this module's to guess at.
|
|
64
|
+
*
|
|
65
|
+
* Duplicates are collapsed: two listings concatenated would otherwise
|
|
66
|
+
* make every queue-id match ambiguous against a stub and itself.
|
|
67
|
+
*/
|
|
68
|
+
export declare function planStubsFromFileNames(fileNames: readonly string[]): string[];
|
|
69
|
+
/** The leading queue id of a stub, or null when it carries none. */
|
|
70
|
+
export declare function queueIdOf(stub: string | null | undefined): string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Resolves a branch stub against the known plan stubs.
|
|
73
|
+
*
|
|
74
|
+
* Exact first, then a queue id that reaches exactly one plan. Anything
|
|
75
|
+
* else answers `none` or `ambiguous` with its candidates — a report
|
|
76
|
+
* would rather show an unattributed bucket than attribute a plan's
|
|
77
|
+
* spend to the wrong plan.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolvePlanStub(branchStub: string | null | undefined, planStubs: readonly string[]): PlanStubResolution;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The build-aside convention: a SQLite store is never changed in place.
|
|
3
|
+
* A new file is built beside it, checked, and swapped in behind a
|
|
4
|
+
* backup of the whole store, or deleted under a dry run.
|
|
5
|
+
*
|
|
6
|
+
* ## The steps
|
|
7
|
+
*
|
|
8
|
+
* 1. Refuse while a rollback journal or write-ahead log sits beside the
|
|
9
|
+
* store ({@link refuseInFlight}): either means a write in flight or
|
|
10
|
+
* interrupted, and a file built from the store then would miss it.
|
|
11
|
+
* 2. Refuse when the parallel file or the backup is already there
|
|
12
|
+
* ({@link rebuildAside}), so two runs never overwrite each other.
|
|
13
|
+
* 3. Build the parallel file. What goes into it is the caller's: the
|
|
14
|
+
* repair (`fix-schema.ts`) brings a fresh file to this rafa's
|
|
15
|
+
* migrations through the log-aware apply and copies the known tables
|
|
16
|
+
* into it, and the forward move (`migrate.ts`) writes the store out
|
|
17
|
+
* with `VACUUM INTO` and applies its pending migrations there, and
|
|
18
|
+
* the merge (`merge-store.ts`) writes the store out the same way and
|
|
19
|
+
* unions another store's rows into it. The caller checks it with
|
|
20
|
+
* {@link checkedCounts}, every table's row count against the
|
|
21
|
+
* store's plus the rows the caller says it added, and
|
|
22
|
+
* {@link refuseCorrupt}, SQLite's `integrity_check`.
|
|
23
|
+
* 4. Any failure while building removes the parallel file and its
|
|
24
|
+
* journal, and leaves the store untouched.
|
|
25
|
+
* 5. Under a dry run the parallel file is then deleted, so the run can
|
|
26
|
+
* be repeated and leaves the directory as it found it. Otherwise the
|
|
27
|
+
* parallel file is swapped in ({@link swapIn}) in four steps:
|
|
28
|
+
* a. The store is written out to the backup with `VACUUM INTO` on a
|
|
29
|
+
* read-only connection (`vacuumInto`, `copy.ts`), and the backup
|
|
30
|
+
* is flushed with `fsync`, since SQLite's documentation says
|
|
31
|
+
* `VACUUM INTO` does not sync the file it writes.
|
|
32
|
+
* b. The store's identity is carried onto the parallel file
|
|
33
|
+
* (`carryStoreIdentity`, `store-meta.ts`): when a write to the
|
|
34
|
+
* live store would keep its origin, the parallel file's row is
|
|
35
|
+
* given the parallel file's own device and inode and a new
|
|
36
|
+
* generation. Otherwise nothing is carried: the parallel file's
|
|
37
|
+
* row is given only a fresh generation the side record does not
|
|
38
|
+
* hold, and the swapped-in store mints on its next write.
|
|
39
|
+
* c. When the carry answered `carried` with a generation, that
|
|
40
|
+
* generation is written to the store's side record
|
|
41
|
+
* (`writeStoreGeneration`, `store-generation.ts`), immediately
|
|
42
|
+
* before the rename. A carry onto a file with no `generation`
|
|
43
|
+
* column answers none, and this step is skipped.
|
|
44
|
+
* d. The parallel file is renamed over the store, which replaces it
|
|
45
|
+
* in one step.
|
|
46
|
+
*
|
|
47
|
+
* ## A failed swap
|
|
48
|
+
*
|
|
49
|
+
* The store's file is only read until the rename, and a rename that
|
|
50
|
+
* fails replaces nothing, so a failure at any of the four steps leaves
|
|
51
|
+
* the store's file as it was. The swap then deletes nothing: it throws a
|
|
52
|
+
* {@link SwapFailure} naming the step, the rebuilt file and the backup,
|
|
53
|
+
* and leaves both beside the store. After a failed backup the backup
|
|
54
|
+
* may be partial or absent; after a failed carry, side record write or
|
|
55
|
+
* rename it holds the whole store. A run with another stamp names other
|
|
56
|
+
* files, so it never overwrites either.
|
|
57
|
+
*
|
|
58
|
+
* The side record is the one thing beside the store a swap writes
|
|
59
|
+
* before the rename. A side record write that fails leaves the record
|
|
60
|
+
* as it was (`writeStoreGeneration` replaces it whole or not at all), so
|
|
61
|
+
* the store keeps its origin on its next write. A rename that fails
|
|
62
|
+
* after it leaves the store's file holding the old generation beside a
|
|
63
|
+
* side record holding the new one, so the store mints once on its next
|
|
64
|
+
* write, which is the safe side, and keeps that id on the write after.
|
|
65
|
+
*
|
|
66
|
+
* ## The backup is a copy, and takes a new origin
|
|
67
|
+
*
|
|
68
|
+
* The backup holds every row the store held, with its schema and
|
|
69
|
+
* `user_version`, but it is a new file: its bytes need not equal the
|
|
70
|
+
* original's, and it has an inode of its own, since it was written while
|
|
71
|
+
* the original still existed. Its `store_meta` row still names the
|
|
72
|
+
* original's inode, so a backup renamed back over the store mints a new
|
|
73
|
+
* origin on its next writing open, as any restored copy must
|
|
74
|
+
* (`store-identity.ts`). It holds the generation the side record held
|
|
75
|
+
* before the swap, which the swap rotated past, so it mints even when
|
|
76
|
+
* the host gives it back the original's inode number. The swapped-in
|
|
77
|
+
* file keeps the store's origin, since the carry recorded the inode the
|
|
78
|
+
* rename brings to the store's path and the generation the swap wrote to
|
|
79
|
+
* its side record.
|
|
80
|
+
*
|
|
81
|
+
* The caller names both files, so each command keeps its own spelling
|
|
82
|
+
* of them beside the store. The checks work over a handle the caller
|
|
83
|
+
* opened; the swap opens the store read-only for the backup and the
|
|
84
|
+
* parallel file for the carry.
|
|
85
|
+
*/
|
|
86
|
+
import type { CarryOutcome } from './store-meta.js';
|
|
87
|
+
import type { Database } from 'bun:sqlite';
|
|
88
|
+
/** A build-aside step refused before the live store was changed. */
|
|
89
|
+
export declare class RebuildRefusal extends Error {
|
|
90
|
+
constructor(message: string, options?: ErrorOptions);
|
|
91
|
+
}
|
|
92
|
+
/** A table with the rows it holds. */
|
|
93
|
+
export interface TableRows {
|
|
94
|
+
readonly table: string;
|
|
95
|
+
readonly rows: number;
|
|
96
|
+
}
|
|
97
|
+
/** A name quoted as an SQL identifier. */
|
|
98
|
+
export declare function quoted(name: string): string;
|
|
99
|
+
/** One count query's answer. */
|
|
100
|
+
export declare function count(db: Database, sql: string): number;
|
|
101
|
+
/** Refuses while a journal or log beside `path` says a write is in flight or was interrupted. */
|
|
102
|
+
export declare function refuseInFlight(path: string): void;
|
|
103
|
+
/**
|
|
104
|
+
* Every table in `tables` with its rows in `main`, refusing one whose
|
|
105
|
+
* count differs from the same table in the attached `original` schema
|
|
106
|
+
* plus the rows `added` names for it, none for a table it does not name.
|
|
107
|
+
* A repair or a migration adds none; a merge names what it inserted.
|
|
108
|
+
*/
|
|
109
|
+
export declare function checkedCounts(db: Database, tables: readonly string[], original: string, added?: Readonly<Record<string, number>>): TableRows[];
|
|
110
|
+
/** Refuses a rebuilt file SQLite does not vouch for. */
|
|
111
|
+
export declare function refuseCorrupt(db: Database): void;
|
|
112
|
+
/** A step of {@link swapIn}, in the order it runs them. */
|
|
113
|
+
export type SwapStep = 'backup' | 'carry' | 'record' | 'rename';
|
|
114
|
+
/** The files one swap works with. */
|
|
115
|
+
interface SwapFiles {
|
|
116
|
+
readonly path: string;
|
|
117
|
+
readonly parallelPath: string;
|
|
118
|
+
readonly backupPath: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A swap that failed before the rename replaced the store: the store is
|
|
122
|
+
* unchanged, and the rebuilt file and whatever the backup step wrote
|
|
123
|
+
* are left beside it. The failure the step threw is the `cause`.
|
|
124
|
+
*/
|
|
125
|
+
export declare class SwapFailure extends RebuildRefusal {
|
|
126
|
+
readonly step: SwapStep;
|
|
127
|
+
readonly parallelPath: string;
|
|
128
|
+
readonly backupPath: string;
|
|
129
|
+
constructor(step: SwapStep, files: SwapFiles, cause: unknown);
|
|
130
|
+
}
|
|
131
|
+
/** The four steps of a swap, which {@link swapIn} runs in this order. */
|
|
132
|
+
export interface SwapSteps {
|
|
133
|
+
/** Writes the store at `path` out to `backupPath`. */
|
|
134
|
+
readonly backup: (path: string, backupPath: string) => void;
|
|
135
|
+
/** Carries the identity of the store at `path` onto the file at `parallelPath`, answering what it wrote. */
|
|
136
|
+
readonly carry: (path: string, parallelPath: string) => CarryOutcome;
|
|
137
|
+
/** Writes `generation` to the side record of the store at `path`. */
|
|
138
|
+
readonly record: (path: string, generation: string) => void;
|
|
139
|
+
/** Renames `parallelPath` over `path`. */
|
|
140
|
+
readonly rename: (parallelPath: string, path: string) => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Swaps the parallel file in over the store at `path`: writes the
|
|
144
|
+
* backup, carries the store's identity, writes the generation the carry
|
|
145
|
+
* answered to the store's side record when it answered `carried` with
|
|
146
|
+
* one, and renames the parallel file over the store. A failed step
|
|
147
|
+
* throws a {@link SwapFailure} and leaves the store's file unchanged, and
|
|
148
|
+
* the backup and the parallel file where they are. `steps` replaces any
|
|
149
|
+
* of the four, so a test can fail one. See the module note.
|
|
150
|
+
*/
|
|
151
|
+
export declare function swapIn(path: string, parallelPath: string, backupPath: string, steps?: Partial<SwapSteps>): void;
|
|
152
|
+
/** What one build-aside run is handed. */
|
|
153
|
+
export interface RebuildAsideOptions<Built> {
|
|
154
|
+
/** The live store's file. */
|
|
155
|
+
readonly path: string;
|
|
156
|
+
/** The file built beside it. */
|
|
157
|
+
readonly parallelPath: string;
|
|
158
|
+
/** Where the backup of the store is written when the rebuild is swapped in. */
|
|
159
|
+
readonly backupPath: string;
|
|
160
|
+
/** Build and check, then delete the parallel file instead of swapping it in. */
|
|
161
|
+
readonly dryRun: boolean;
|
|
162
|
+
/** Builds and checks the parallel file, throwing to refuse it. */
|
|
163
|
+
readonly build: (parallelPath: string) => Built;
|
|
164
|
+
}
|
|
165
|
+
/** What the build answered, and where the backup was written: null under a dry run. */
|
|
166
|
+
export interface RebuildAsideResult<Built> {
|
|
167
|
+
readonly built: Built;
|
|
168
|
+
readonly backupPath: string | null;
|
|
169
|
+
}
|
|
170
|
+
/** Runs the build-aside steps around `options.build`. See the module note. */
|
|
171
|
+
export declare function rebuildAside<Built>(options: RebuildAsideOptions<Built>): RebuildAsideResult<Built>;
|
|
172
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compatibility decision: whether this rafa may use a store, given
|
|
3
|
+
* the named migrations the store holds and the ones this build knows.
|
|
4
|
+
*
|
|
5
|
+
* {@link planSchema} is a pure function over plain values. It imports
|
|
6
|
+
* nothing from `bun:sqlite` or `sqlite.ts`, so the decision never
|
|
7
|
+
* depends on the backend that holds the store. The caller reads the
|
|
8
|
+
* store's migration log and `user_version` and passes them in. It
|
|
9
|
+
* answers either "use", with what an open has to adopt and apply and
|
|
10
|
+
* the `user_version` the store ends at, or "refuse", with one of the
|
|
11
|
+
* seven {@link RefusalReason}s.
|
|
12
|
+
*
|
|
13
|
+
* ## How it decides
|
|
14
|
+
*
|
|
15
|
+
* A store with no log is a store a release before the log wrote, and
|
|
16
|
+
* its `user_version` counts the legacy entries it holds. From 1 to
|
|
17
|
+
* {@link LEGACY_GATE_OPEN} it is adopted: those entries are recorded
|
|
18
|
+
* with this build's checksums, which match what ran because a shipped
|
|
19
|
+
* entry is frozen. A version of 0 is a fresh store. Anything else was
|
|
20
|
+
* written by no released rafa.
|
|
21
|
+
*
|
|
22
|
+
* A store with a log is judged from the log, never from a number. A
|
|
23
|
+
* logged id this build knows must carry the checksum this build holds.
|
|
24
|
+
* A logged id this build does not know is read by its `breaks`:
|
|
25
|
+
* nothing lets this rafa use the store as it is; `writers` lets it
|
|
26
|
+
* read and refuses a write; `readers`, or a word this rafa does not
|
|
27
|
+
* know, refuses the store. An open that has migrations to apply writes
|
|
28
|
+
* the store, whatever its caller asked for, so it counts as a write
|
|
29
|
+
* here too.
|
|
30
|
+
*
|
|
31
|
+
* A pending migration this build knows is applied late when it is
|
|
32
|
+
* additive, which is how two branches that each append one merge in
|
|
33
|
+
* either order. A pending breaking migration applies only in order,
|
|
34
|
+
* and only through `rafa effort migrate`.
|
|
35
|
+
*
|
|
36
|
+
* The seven rows are tried in {@link REFUSAL_REASONS} order and the
|
|
37
|
+
* first that matches decides. Every refusal's message ends with its
|
|
38
|
+
* {@link SchemaRefusal.nextStep}, the one command to run next.
|
|
39
|
+
*/
|
|
40
|
+
import type { MigrationSpec, SqliteMigration } from './migrations.js';
|
|
41
|
+
/** Whether an open reads the store only, or also writes it. */
|
|
42
|
+
export type StoreAccess = 'read' | 'write';
|
|
43
|
+
/** Who asks: an ordinary open, or `rafa effort migrate`. */
|
|
44
|
+
export type SchemaCaller = 'open' | 'migrate';
|
|
45
|
+
/** A migration this build knows, with the checksum of its body. */
|
|
46
|
+
export interface CatalogueEntry extends MigrationSpec {
|
|
47
|
+
/** Lowercase sha256 hex of the body the backend runs. */
|
|
48
|
+
readonly sha256: string;
|
|
49
|
+
}
|
|
50
|
+
/** One row of a store's migration log, as the caller read it. */
|
|
51
|
+
export interface LoggedMigration {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly sha256: string;
|
|
54
|
+
/** The words recorded, which may include one this rafa does not know. */
|
|
55
|
+
readonly breaks: readonly string[];
|
|
56
|
+
readonly appliedAt: string;
|
|
57
|
+
readonly appliedBy: string;
|
|
58
|
+
}
|
|
59
|
+
/** What the caller read from the store before any write. */
|
|
60
|
+
export interface StoreSchema {
|
|
61
|
+
/** The store's path, as every refusal names it. */
|
|
62
|
+
readonly path: string;
|
|
63
|
+
/** `PRAGMA user_version`: the legacy gate, or a pre-log store's count. */
|
|
64
|
+
readonly userVersion: number;
|
|
65
|
+
/** The migration log in apply order, or null when the store has none. */
|
|
66
|
+
readonly log: readonly LoggedMigration[] | null;
|
|
67
|
+
}
|
|
68
|
+
/** The seven reasons a store is refused, in the order they are tried. */
|
|
69
|
+
export declare const REFUSAL_REASONS: readonly ["pre-log-unreleased", "gate-mismatch", "edited", "unknown-breaks-readers", "unknown-breaks-writers", "breaking-out-of-order", "breaking-pending"];
|
|
70
|
+
/** Why this rafa refuses a store. */
|
|
71
|
+
export type RefusalReason = typeof REFUSAL_REASONS[number];
|
|
72
|
+
/** This rafa may use the store once it has adopted and applied what is listed. */
|
|
73
|
+
export interface SchemaUse {
|
|
74
|
+
readonly verdict: 'use';
|
|
75
|
+
/** Legacy entries a log-less store holds, to be logged without running. */
|
|
76
|
+
readonly adopted: readonly CatalogueEntry[];
|
|
77
|
+
/** Entries to apply, in catalogue order. */
|
|
78
|
+
readonly pending: readonly CatalogueEntry[];
|
|
79
|
+
/** Logged migrations this build does not know; each is additive, or breaks writers only on a read. */
|
|
80
|
+
readonly unknown: readonly LoggedMigration[];
|
|
81
|
+
/** The `user_version` the store holds once everything listed is applied. */
|
|
82
|
+
readonly gate: number;
|
|
83
|
+
}
|
|
84
|
+
/** This rafa refuses the store, and says why and what to run next. */
|
|
85
|
+
export interface SchemaRefusal {
|
|
86
|
+
readonly verdict: 'refuse';
|
|
87
|
+
readonly reason: RefusalReason;
|
|
88
|
+
/** The refusal text, ending with `Next safe step: <nextStep>`. */
|
|
89
|
+
readonly message: string;
|
|
90
|
+
/** The one command to run next. */
|
|
91
|
+
readonly nextStep: string;
|
|
92
|
+
}
|
|
93
|
+
/** What {@link planSchema} answers. */
|
|
94
|
+
export type SchemaPlan = SchemaUse | SchemaRefusal;
|
|
95
|
+
/** A migration's SQL entries with the checksums {@link planSchema} compares. */
|
|
96
|
+
export declare function sqliteCatalogue(migrations: readonly SqliteMigration[]): readonly CatalogueEntry[];
|
|
97
|
+
/**
|
|
98
|
+
* Decide whether this rafa may use `store` with this build's
|
|
99
|
+
* `catalogue`, for an open with `access` from `caller`. The module note
|
|
100
|
+
* says how.
|
|
101
|
+
*/
|
|
102
|
+
export declare function planSchema(store: StoreSchema, catalogue: readonly CatalogueEntry[], access: StoreAccess, caller: SchemaCaller): SchemaPlan;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session row's own types: what a session was dispatched to do and
|
|
3
|
+
* the stats accumulated over its log.
|
|
4
|
+
*
|
|
5
|
+
* Moved down from `src/effort/classify.ts` (`SessionKind`) and
|
|
6
|
+
* `src/effort/session-log.ts` (`SessionStats` and the
|
|
7
|
+
* `SessionUsageTotals` it names), which classify and read session logs
|
|
8
|
+
* and re-export these names under the same names: the store's row types
|
|
9
|
+
* (`types.ts`) need the shapes but not the classifier or the reader, so
|
|
10
|
+
* they import them here and the store imports nothing above core for
|
|
11
|
+
* them. The module notes of `classify.ts` and `session-log.ts` still
|
|
12
|
+
* describe how each value is derived from a log.
|
|
13
|
+
*/
|
|
14
|
+
/** What a session was dispatched to do. */
|
|
15
|
+
export type SessionKind = 'task' | 'plan-generation' | 'wrap-up' | 'ci-repair' | 'search' | 'other';
|
|
16
|
+
/**
|
|
17
|
+
* Every numeric counter under a record's `message.usage`, summed over
|
|
18
|
+
* the whole session. Names are the camelCase of the wire spelling;
|
|
19
|
+
* the two `ephemeral*` members are the `cache_creation` split and the
|
|
20
|
+
* two `*Requests` members the `server_tool_use` one.
|
|
21
|
+
*/
|
|
22
|
+
export interface SessionUsageTotals {
|
|
23
|
+
inputTokens: number;
|
|
24
|
+
outputTokens: number;
|
|
25
|
+
cacheCreationInputTokens: number;
|
|
26
|
+
cacheReadInputTokens: number;
|
|
27
|
+
ephemeral1hInputTokens: number;
|
|
28
|
+
ephemeral5mInputTokens: number;
|
|
29
|
+
webSearchRequests: number;
|
|
30
|
+
webFetchRequests: number;
|
|
31
|
+
thinkingTokens: number;
|
|
32
|
+
}
|
|
33
|
+
/** One accumulated stats row per session log. */
|
|
34
|
+
export interface SessionStats {
|
|
35
|
+
/** Log basename without its extension, which is the session uuid. */
|
|
36
|
+
sessionId: string;
|
|
37
|
+
/** The path the row was accumulated from. */
|
|
38
|
+
filePath: string;
|
|
39
|
+
/** Non-empty lines read. */
|
|
40
|
+
lineCount: number;
|
|
41
|
+
/** Lines that parsed to a JSON object. */
|
|
42
|
+
recordCount: number;
|
|
43
|
+
/** Lines that did not — malformed JSON, or JSON that is not an object. */
|
|
44
|
+
unparsedLineCount: number;
|
|
45
|
+
/** Records by top-level `type`; an absent type is keyed as `unknown`. */
|
|
46
|
+
recordTypeCounts: Record<string, number>;
|
|
47
|
+
/** Records with `type: assistant`, which are the billable turns. */
|
|
48
|
+
assistantRecordCount: number;
|
|
49
|
+
/** Earliest and latest `timestamp` by instant, not by file order. */
|
|
50
|
+
firstTimestamp: string | null;
|
|
51
|
+
lastTimestamp: string | null;
|
|
52
|
+
/** Records per top-level `gitBranch`, the plan-attribution field. */
|
|
53
|
+
gitBranchCounts: Record<string, number>;
|
|
54
|
+
/** Records per `entrypoint`; separates loop from hand-driven traffic. */
|
|
55
|
+
entrypointCounts: Record<string, number>;
|
|
56
|
+
/** Records per top-level `effort`; what a session actually ran at. */
|
|
57
|
+
effortCounts: Record<string, number>;
|
|
58
|
+
/** Records carrying `isSidechain: true`, which are subagent turns. */
|
|
59
|
+
sidechainRecordCount: number;
|
|
60
|
+
/** Records per `message.model`. */
|
|
61
|
+
modelCounts: Record<string, number>;
|
|
62
|
+
/** Summed `message.usage` counters. */
|
|
63
|
+
usage: SessionUsageTotals;
|
|
64
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** What every effort store open reads. */
|
|
2
|
+
export interface StoreSettings {
|
|
3
|
+
/** How long an open waits for another process's write lock, in ms. */
|
|
4
|
+
readonly busyTimeoutMs: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Sets the settings every later open reads; `null` puts the default
|
|
8
|
+
* back. Throws a `RangeError`, setting nothing, for a busy timeout
|
|
9
|
+
* outside the whole numbers the config accepts.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setActiveStoreSettings(settings: StoreSettings | null): void;
|
|
12
|
+
/** The settings every open reads now; see the module note. */
|
|
13
|
+
export declare function activeStoreSettings(): StoreSettings;
|