@cat-factory/local-server 0.92.1 → 0.93.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/dist/container.d.ts.map +1 -1
- package/dist/container.js +133 -85
- package/dist/container.js.map +1 -1
- package/dist/harnessImage.d.ts +1 -1
- package/dist/harnessImage.js +1 -1
- package/dist/mothership.d.ts +10 -1
- package/dist/mothership.d.ts.map +1 -1
- package/dist/mothership.js +6 -1
- package/dist/mothership.js.map +1 -1
- package/dist/sqlite/telemetryIngestReader.d.ts +62 -0
- package/dist/sqlite/telemetryIngestReader.d.ts.map +1 -0
- package/dist/sqlite/telemetryIngestReader.js +86 -0
- package/dist/sqlite/telemetryIngestReader.js.map +1 -0
- package/dist/sqlite/telemetryRows.d.ts +62 -0
- package/dist/sqlite/telemetryRows.d.ts.map +1 -0
- package/dist/sqlite/telemetryRows.js +87 -0
- package/dist/sqlite/telemetryRows.js.map +1 -0
- package/dist/sqlite/telemetryStore.d.ts +3 -1
- package/dist/sqlite/telemetryStore.d.ts.map +1 -1
- package/dist/sqlite/telemetryStore.js +87 -87
- package/dist/sqlite/telemetryStore.js.map +1 -1
- package/dist/telemetryIngest.d.ts +49 -0
- package/dist/telemetryIngest.d.ts.map +1 -0
- package/dist/telemetryIngest.js +226 -0
- package/dist/telemetryIngest.js.map +1 -0
- package/dist/telemetryRetention.d.ts +2 -0
- package/dist/telemetryRetention.d.ts.map +1 -1
- package/dist/telemetryRetention.js +5 -0
- package/dist/telemetryRetention.js.map +1 -1
- package/dist/telemetrySweeps.d.ts +28 -0
- package/dist/telemetrySweeps.d.ts.map +1 -0
- package/dist/telemetrySweeps.js +32 -0
- package/dist/telemetrySweeps.js.map +1 -0
- package/package.json +12 -12
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Clock, Logger } from '@cat-factory/kernel';
|
|
2
|
+
import { type MachineTelemetryClient } from '@cat-factory/server';
|
|
3
|
+
import type { LocalTelemetryIngestReader } from './sqlite/telemetryIngestReader.js';
|
|
4
|
+
/** A row no batch can ever carry, named so the sweep can REPORT the drop rather than hide it. */
|
|
5
|
+
export interface OversizedRow {
|
|
6
|
+
id: string;
|
|
7
|
+
chars: number;
|
|
8
|
+
}
|
|
9
|
+
/** What one sweep moved, for logging. */
|
|
10
|
+
export interface TelemetryIngestSweepResult {
|
|
11
|
+
runs: number;
|
|
12
|
+
metrics: number;
|
|
13
|
+
snapshots: number;
|
|
14
|
+
searchQueries: number;
|
|
15
|
+
/** Runs left un-marked because their upload failed — retried on the next pass. */
|
|
16
|
+
failed: number;
|
|
17
|
+
/**
|
|
18
|
+
* Rows skipped because one row alone exceeds the mothership's whole-body cap. Reported, never
|
|
19
|
+
* silent: the alternative is a run that can never finish draining, and a cap that drops
|
|
20
|
+
* something has to say so.
|
|
21
|
+
*/
|
|
22
|
+
skipped: number;
|
|
23
|
+
}
|
|
24
|
+
export interface TelemetryIngestDeps {
|
|
25
|
+
reader: LocalTelemetryIngestReader;
|
|
26
|
+
client: MachineTelemetryClient;
|
|
27
|
+
clock: Clock;
|
|
28
|
+
log: Logger;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Upload every quiesced run's telemetry once. Pure over its dependencies (no timer), so it is
|
|
32
|
+
* unit-testable directly — the same shape as `sweepLocalTelemetryRetention`.
|
|
33
|
+
*
|
|
34
|
+
* A run whose upload fails is left UNMARKED and counted in `failed`: the next pass retries it from
|
|
35
|
+
* the beginning, which is safe precisely because the mothership's append is idempotent by row id.
|
|
36
|
+
* One run's failure never stops the pass — a single unreachable moment must not park the backlog.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sweepTelemetryIngest(deps: TelemetryIngestDeps, now: number): Promise<TelemetryIngestSweepResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Start the periodic telemetry ingest sweep. Best-effort throughout — losing observability must
|
|
41
|
+
* never take the node down, and an unreachable mothership only means the rows stay local until it
|
|
42
|
+
* comes back.
|
|
43
|
+
*
|
|
44
|
+
* Like the retention sweep, the returned stop function is ASYNC and must be AWAITED before the
|
|
45
|
+
* telemetry store is closed: a pass in flight when shutdown closes the SQLite handle would die on
|
|
46
|
+
* "database is not open" and put a spurious error line on every clean exit.
|
|
47
|
+
*/
|
|
48
|
+
export declare function startTelemetryIngest(deps: TelemetryIngestDeps): () => Promise<void>;
|
|
49
|
+
//# sourceMappingURL=telemetryIngest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetryIngest.d.ts","sourceRoot":"","sources":["../src/telemetryIngest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,EAIL,KAAK,sBAAsB,EAE5B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAgB,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AAyDjG,iGAAiG;AACjG,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,0BAA0B,CAAA;IAClC,MAAM,EAAE,sBAAsB,CAAA;IAC9B,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAoFD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,0BAA0B,CAAC,CAqErC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CA6BnF"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { startSweeper } from '@cat-factory/node-server';
|
|
2
|
+
import { MAX_TELEMETRY_INGEST_CHARS, MachineTokenUnavailableError, TELEMETRY_INGEST_LIMITS, } from '@cat-factory/server';
|
|
3
|
+
// The UPSTREAM half of the mothership-mode telemetry bucket (docs/initiatives/mothership-mode.md,
|
|
4
|
+
// PR 5). The capture half writes every LLM call, dispatch context and performed search to the
|
|
5
|
+
// laptop's `node:sqlite` store, because putting a network round trip on the hot path of every
|
|
6
|
+
// model call is exactly what the local-first bucket exists to avoid. This is the background sweep
|
|
7
|
+
// that carries a FINISHED run's rows up to the mothership afterwards, so hosted teammates can read
|
|
8
|
+
// the observability of a run a developer drove, and so those rows outlive the node's short local
|
|
9
|
+
// retention window.
|
|
10
|
+
//
|
|
11
|
+
// Two design points worth keeping:
|
|
12
|
+
//
|
|
13
|
+
// - "Finished" is QUIESCENCE, not a run-status read. The node holds no execution index of its
|
|
14
|
+
// own (runs live on the mothership), so asking "which runs ended" would mean a remote query
|
|
15
|
+
// per candidate — the N+1 this bucket was created to avoid. A run that has produced no
|
|
16
|
+
// telemetry for the grace period is done as far as its telemetry is concerned, and a RESUMED
|
|
17
|
+
// run simply becomes a candidate again on its next quiet period.
|
|
18
|
+
// - Progress is a per-run high-water mark, advanced only after the whole run drained. A node
|
|
19
|
+
// that dies mid-drain re-offers the rows it already sent, which costs bandwidth and nothing
|
|
20
|
+
// else: the mothership's append is idempotent by row id.
|
|
21
|
+
/** How often the sweep runs. Telemetry is read after the fact, so a slow cadence is right. */
|
|
22
|
+
const INGEST_SWEEP_INTERVAL_MS = 5 * 60 * 1000;
|
|
23
|
+
/**
|
|
24
|
+
* How long a run must produce no telemetry before it is uploaded. Long enough that an agent step
|
|
25
|
+
* thinking between model calls is not mistaken for a finished run (which would upload the run
|
|
26
|
+
* repeatedly as it continued), short enough that a developer's teammates see a run's telemetry
|
|
27
|
+
* while it is still the thing being discussed.
|
|
28
|
+
*/
|
|
29
|
+
const QUIESCENCE_MS = 10 * 60 * 1000;
|
|
30
|
+
/** Runs uploaded per pass — a bound on how much a single sweep can spend on a backlog. */
|
|
31
|
+
const RUNS_PER_SWEEP = 20;
|
|
32
|
+
/**
|
|
33
|
+
* Rows read per page, per sink. Deliberately the mothership's own per-request ROW caps: a page the
|
|
34
|
+
* node built larger would be refused whole, and the drain would never advance.
|
|
35
|
+
*/
|
|
36
|
+
const PAGE_SIZES = TELEMETRY_INGEST_LIMITS;
|
|
37
|
+
/**
|
|
38
|
+
* Headroom left under {@link MAX_TELEMETRY_INGEST_CHARS} for everything in the request body that
|
|
39
|
+
* is not the rows themselves — the envelope's two ids, the sink key, the brackets and separators.
|
|
40
|
+
* Generous, because being a few hundred bytes over the mothership's cap costs a whole refused
|
|
41
|
+
* batch.
|
|
42
|
+
*/
|
|
43
|
+
const ENVELOPE_OVERHEAD_CHARS = 4_096;
|
|
44
|
+
/**
|
|
45
|
+
* The byte budget one posted batch may fill. The mothership enforces TWO caps — a row count AND
|
|
46
|
+
* {@link MAX_TELEMETRY_INGEST_CHARS} — and mirroring only the first is what let a page of 20
|
|
47
|
+
* snapshot rows (routinely megabytes each) exceed the byte cap, be refused 413 whole, and leave
|
|
48
|
+
* the run retrying the same doomed page every sweep until the local prune deleted it.
|
|
49
|
+
*/
|
|
50
|
+
const BATCH_BUDGET_CHARS = MAX_TELEMETRY_INGEST_CHARS - ENVELOPE_OVERHEAD_CHARS;
|
|
51
|
+
/** The last row of a page, as the cursor the next page resumes from. */
|
|
52
|
+
function cursorOf(rows) {
|
|
53
|
+
const last = rows[rows.length - 1];
|
|
54
|
+
return last ? { createdAt: last.createdAt, id: last.id } : undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Split one page into batches that each fit {@link BATCH_BUDGET_CHARS}, separating out any row
|
|
58
|
+
* that cannot fit ALONE.
|
|
59
|
+
*
|
|
60
|
+
* The row caps bound COUNT; this bounds BYTES, which is the axis a snapshot row moves — the whole
|
|
61
|
+
* composed prompt plus every injected context file's body. Sizing is by the row's own serialized
|
|
62
|
+
* length, the same measure the mothership applies to the body it receives.
|
|
63
|
+
*
|
|
64
|
+
* A row too big even by itself is returned in `oversized` rather than posted: sending it means a
|
|
65
|
+
* guaranteed 413, which fails the run's whole drain and re-offers the identical page on the next
|
|
66
|
+
* sweep, forever. Skipping it lets the rest of the run through, and the caller reports the drop.
|
|
67
|
+
*/
|
|
68
|
+
function budgetBatches(rows) {
|
|
69
|
+
const batches = [];
|
|
70
|
+
const oversized = [];
|
|
71
|
+
let batch = [];
|
|
72
|
+
let used = 0;
|
|
73
|
+
for (const row of rows) {
|
|
74
|
+
const chars = JSON.stringify(row).length;
|
|
75
|
+
if (chars > BATCH_BUDGET_CHARS) {
|
|
76
|
+
oversized.push({ id: row.id, chars });
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (batch.length > 0 && used + chars > BATCH_BUDGET_CHARS) {
|
|
80
|
+
batches.push(batch);
|
|
81
|
+
batch = [];
|
|
82
|
+
used = 0;
|
|
83
|
+
}
|
|
84
|
+
batch.push(row);
|
|
85
|
+
used += chars;
|
|
86
|
+
}
|
|
87
|
+
if (batch.length > 0)
|
|
88
|
+
batches.push(batch);
|
|
89
|
+
return { batches, oversized };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Drain ONE sink of one run, posting each page as one or more byte-budgeted batches. Pages are
|
|
93
|
+
* read forwards on the `(createdAt, id)` keyset, so a run's telemetry lands on the mothership in
|
|
94
|
+
* the order it was captured — which is what keeps a metric's prompt-delta chain readable there.
|
|
95
|
+
*
|
|
96
|
+
* The cursor advances past a skipped oversized row deliberately: it can never be uploaded, so
|
|
97
|
+
* holding the drain on it would strand every row behind it too. The caller names what was dropped.
|
|
98
|
+
*/
|
|
99
|
+
async function drainSink(read, limit, post) {
|
|
100
|
+
let cursor;
|
|
101
|
+
const drain = { uploaded: 0, oversized: [] };
|
|
102
|
+
for (;;) {
|
|
103
|
+
const rows = read(cursor, limit);
|
|
104
|
+
if (rows.length === 0)
|
|
105
|
+
return drain;
|
|
106
|
+
const budgeted = budgetBatches(rows);
|
|
107
|
+
for (const batch of budgeted.batches) {
|
|
108
|
+
await post(batch);
|
|
109
|
+
drain.uploaded += batch.length;
|
|
110
|
+
}
|
|
111
|
+
drain.oversized.push(...budgeted.oversized);
|
|
112
|
+
cursor = cursorOf(rows);
|
|
113
|
+
// A short page is the last one; asking again would cost a query to learn nothing.
|
|
114
|
+
if (rows.length < limit)
|
|
115
|
+
return drain;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Upload every quiesced run's telemetry once. Pure over its dependencies (no timer), so it is
|
|
120
|
+
* unit-testable directly — the same shape as `sweepLocalTelemetryRetention`.
|
|
121
|
+
*
|
|
122
|
+
* A run whose upload fails is left UNMARKED and counted in `failed`: the next pass retries it from
|
|
123
|
+
* the beginning, which is safe precisely because the mothership's append is idempotent by row id.
|
|
124
|
+
* One run's failure never stops the pass — a single unreachable moment must not park the backlog.
|
|
125
|
+
*/
|
|
126
|
+
export async function sweepTelemetryIngest(deps, now) {
|
|
127
|
+
const pending = deps.reader.listPendingRuns(now - QUIESCENCE_MS, RUNS_PER_SWEEP);
|
|
128
|
+
const result = {
|
|
129
|
+
runs: 0,
|
|
130
|
+
metrics: 0,
|
|
131
|
+
snapshots: 0,
|
|
132
|
+
searchQueries: 0,
|
|
133
|
+
failed: 0,
|
|
134
|
+
skipped: 0,
|
|
135
|
+
};
|
|
136
|
+
for (const run of pending) {
|
|
137
|
+
const { workspaceId, executionId } = run;
|
|
138
|
+
const send = (batch) => deps.client.ingest({ workspaceId, executionId, ...batch }).then(() => undefined);
|
|
139
|
+
try {
|
|
140
|
+
const metrics = await drainSink((cursor, limit) => deps.reader.listMetrics(workspaceId, executionId, cursor, limit), PAGE_SIZES.metrics, (rows) => send({ metrics: rows }));
|
|
141
|
+
const snapshots = await drainSink((cursor, limit) => deps.reader.listSnapshots(workspaceId, executionId, cursor, limit), PAGE_SIZES.snapshots, (rows) => send({ snapshots: rows }));
|
|
142
|
+
const searchQueries = await drainSink((cursor, limit) => deps.reader.listSearchQueries(workspaceId, executionId, cursor, limit), PAGE_SIZES.searchQueries, (rows) => send({ searchQueries: rows }));
|
|
143
|
+
result.metrics += metrics.uploaded;
|
|
144
|
+
result.snapshots += snapshots.uploaded;
|
|
145
|
+
result.searchQueries += searchQueries.uploaded;
|
|
146
|
+
// A row too large to ever post is dropped rather than retried forever — but a cap that drops
|
|
147
|
+
// something says what it dropped, naming the rows so the gap in the mothership's view of
|
|
148
|
+
// this run is explicable rather than an unexplained hole.
|
|
149
|
+
const oversized = [...metrics.oversized, ...snapshots.oversized, ...searchQueries.oversized];
|
|
150
|
+
if (oversized.length > 0) {
|
|
151
|
+
result.skipped += oversized.length;
|
|
152
|
+
deps.log.warn('mothership telemetry ingest skipped rows over the body cap', {
|
|
153
|
+
workspaceId,
|
|
154
|
+
executionId,
|
|
155
|
+
skipped: oversized.length,
|
|
156
|
+
capChars: BATCH_BUDGET_CHARS,
|
|
157
|
+
rows: oversized.map((row) => `${row.id}:${row.chars}`).join(','),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Marked only once every sink drained — the mark is what stops the run being re-offered, so
|
|
161
|
+
// writing it after a partial upload would strand whatever had not been sent yet.
|
|
162
|
+
deps.reader.markIngested(workspaceId, executionId, run.lastWriteAt, now);
|
|
163
|
+
result.runs += 1;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
result.failed += 1;
|
|
167
|
+
// No token yet: EVERY remaining run would fail the same way, so the pass stops here with one
|
|
168
|
+
// line instead of twenty identical ones. The runs stay candidates — nothing was marked.
|
|
169
|
+
if (error instanceof MachineTokenUnavailableError) {
|
|
170
|
+
deps.log.info('mothership telemetry ingest deferred: node has not logged in yet', {
|
|
171
|
+
pending: pending.length,
|
|
172
|
+
});
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
deps.log.warn('mothership telemetry ingest failed for a run', {
|
|
176
|
+
workspaceId,
|
|
177
|
+
executionId,
|
|
178
|
+
err: error instanceof Error ? error.message : String(error),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Start the periodic telemetry ingest sweep. Best-effort throughout — losing observability must
|
|
186
|
+
* never take the node down, and an unreachable mothership only means the rows stay local until it
|
|
187
|
+
* comes back.
|
|
188
|
+
*
|
|
189
|
+
* Like the retention sweep, the returned stop function is ASYNC and must be AWAITED before the
|
|
190
|
+
* telemetry store is closed: a pass in flight when shutdown closes the SQLite handle would die on
|
|
191
|
+
* "database is not open" and put a spurious error line on every clean exit.
|
|
192
|
+
*/
|
|
193
|
+
export function startTelemetryIngest(deps) {
|
|
194
|
+
let stopped = false;
|
|
195
|
+
let inFlight;
|
|
196
|
+
const stopSweeper = startSweeper({
|
|
197
|
+
name: 'mothership-telemetry-ingest',
|
|
198
|
+
intervalMs: INGEST_SWEEP_INTERVAL_MS,
|
|
199
|
+
log: deps.log,
|
|
200
|
+
failureMessage: 'mothership telemetry ingest sweep failed',
|
|
201
|
+
tick: async () => {
|
|
202
|
+
if (stopped)
|
|
203
|
+
return;
|
|
204
|
+
const pass = sweepTelemetryIngest(deps, deps.clock.now());
|
|
205
|
+
inFlight = pass;
|
|
206
|
+
try {
|
|
207
|
+
const moved = await pass;
|
|
208
|
+
if (moved.runs > 0 || moved.failed > 0 || moved.skipped > 0) {
|
|
209
|
+
deps.log.info('mothership telemetry ingest uploaded runs', { ...moved });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
if (inFlight === pass)
|
|
214
|
+
inFlight = undefined;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
return async () => {
|
|
219
|
+
stopped = true;
|
|
220
|
+
stopSweeper();
|
|
221
|
+
// silent-catch-ok: the sweeper's own error handler already logged this pass's failure; here we
|
|
222
|
+
// only need it to have FINISHED touching the store before the caller closes it.
|
|
223
|
+
await inFlight?.catch(() => undefined);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=telemetryIngest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetryIngest.js","sourceRoot":"","sources":["../src/telemetryIngest.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,8 @@ export interface LocalTelemetryRetentionResult {
|
|
|
8
8
|
agentSearchQueries: number;
|
|
9
9
|
provisioningLog: number;
|
|
10
10
|
subscriptionQuotaCycles: number;
|
|
11
|
+
/** Upstream-ingest high-water marks (PR 5's sync UP) whose runs' rows are long gone. */
|
|
12
|
+
ingestState: number;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Prune each local telemetry table to its retention window. Pure over the store, so it is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetryRetention.d.ts","sourceRoot":"","sources":["../src/telemetryRetention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AA4BrE,mEAAmE;AACnE,MAAM,WAAW,6BAA6B;IAC5C,cAAc,EAAE,MAAM,CAAA;IACtB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,uBAAuB,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"telemetryRetention.d.ts","sourceRoot":"","sources":["../src/telemetryRetention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AA4BrE,mEAAmE;AACnE,MAAM,WAAW,6BAA6B;IAC5C,cAAc,EAAE,MAAM,CAAA;IACtB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,wFAAwF;IACxF,WAAW,EAAE,MAAM,CAAA;CACpB;AAYD;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,mBAAmB,EAC1B,SAAS,EAAE,eAAe,EAC1B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,6BAA6B,CAAC,CAyBxC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,mBAAmB,EAC1B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,GACV,MAAM,OAAO,CAAC,IAAI,CAAC,CA8BrB"}
|
|
@@ -41,6 +41,11 @@ export async function sweepLocalTelemetryRetention(store, retention, now) {
|
|
|
41
41
|
agentSearchQueries: await prune(retention.llmCallMetricsMs, now, (c) => store.agentSearchQueryRepository.deleteOlderThan(c)),
|
|
42
42
|
provisioningLog: await prune(retention.provisioningLogMs, now, (c) => store.provisioningLogRepository.deleteOlderThan(c)),
|
|
43
43
|
subscriptionQuotaCycles: await prune(SUBSCRIPTION_QUOTA_CYCLE_RETENTION_MS, now, (c) => store.subscriptionQuotaCycleRepository.deleteOlderThan(c)),
|
|
44
|
+
// The ingest bookkeeping rides the LLM-call window too, and the ordering is what makes that
|
|
45
|
+
// safe: a mark is stamped at ingest time, which is always at or after the newest row it covers,
|
|
46
|
+
// so the mark outlives the rows it describes and can never be dropped while a still-stored run
|
|
47
|
+
// would be re-uploaded because of it.
|
|
48
|
+
ingestState: await prune(retention.llmCallMetricsMs, now, async (c) => store.ingestReader.deleteIngestStateOlderThan(c)),
|
|
44
49
|
};
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetryRetention.js","sourceRoot":"","sources":["../src/telemetryRetention.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIvD,gGAAgG;AAChG,4FAA4F;AAC5F,iGAAiG;AACjG,kGAAkG;AAClG,kGAAkG;AAClG,8FAA8F;AAC9F,qCAAqC;AACrC,EAAE;AACF,iGAAiG;AACjG,mGAAmG;AACnG,wFAAwF;AAExF;;;;GAIG;AACH,MAAM,iCAAiC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAExD;;;;GAIG;AACH,MAAM,qCAAqC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"telemetryRetention.js","sourceRoot":"","sources":["../src/telemetryRetention.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Clock, Logger } from '@cat-factory/kernel';
|
|
2
|
+
import type { MachineTelemetryClient, RetentionConfig } from '@cat-factory/server';
|
|
3
|
+
import type { LocalTelemetryStore } from './sqlite/telemetryStore.js';
|
|
4
|
+
export interface MothershipTelemetrySweepDeps {
|
|
5
|
+
store: LocalTelemetryStore;
|
|
6
|
+
client: MachineTelemetryClient;
|
|
7
|
+
retention: RetentionConfig;
|
|
8
|
+
clock: Clock;
|
|
9
|
+
log: Logger;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start the local telemetry PRUNE and the UPSTREAM ingest, returning one stop function that awaits
|
|
13
|
+
* both.
|
|
14
|
+
*
|
|
15
|
+
* - The prune is the only thing bounding the store: the mothership's cron owns ITS tables, and the
|
|
16
|
+
* Node facade's retention sweeper runs from `start()`, which a mothership-mode boot never calls.
|
|
17
|
+
* Left unpruned, `llm_call_metrics` — full per-call prompt + response bodies — grows without
|
|
18
|
+
* bound on the developer's disk.
|
|
19
|
+
* - The ingest is what stops the prune being a DATA LOSS: it carries a quiesced run's rows up to
|
|
20
|
+
* the mothership, so a run this laptop drove stays readable (by hosted teammates, and by anyone
|
|
21
|
+
* at all) after the local window passes.
|
|
22
|
+
*
|
|
23
|
+
* The returned stop is ASYNC and must be AWAITED before the store closes — each sweep's first pass
|
|
24
|
+
* is asynchronous, so an un-awaited one dies on "database is not open" and puts a spurious error
|
|
25
|
+
* line on every clean exit.
|
|
26
|
+
*/
|
|
27
|
+
export declare function startMothershipTelemetrySweeps(deps: MothershipTelemetrySweepDeps): () => Promise<void>;
|
|
28
|
+
//# sourceMappingURL=telemetrySweeps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetrySweeps.d.ts","sourceRoot":"","sources":["../src/telemetrySweeps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAWrE,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,mBAAmB,CAAA;IAC1B,MAAM,EAAE,sBAAsB,CAAA;IAC9B,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,4BAA4B,GACjC,MAAM,OAAO,CAAC,IAAI,CAAC,CAiBrB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { startTelemetryIngest } from './telemetryIngest.js';
|
|
2
|
+
import { startLocalTelemetryRetention } from './telemetryRetention.js';
|
|
3
|
+
/**
|
|
4
|
+
* Start the local telemetry PRUNE and the UPSTREAM ingest, returning one stop function that awaits
|
|
5
|
+
* both.
|
|
6
|
+
*
|
|
7
|
+
* - The prune is the only thing bounding the store: the mothership's cron owns ITS tables, and the
|
|
8
|
+
* Node facade's retention sweeper runs from `start()`, which a mothership-mode boot never calls.
|
|
9
|
+
* Left unpruned, `llm_call_metrics` — full per-call prompt + response bodies — grows without
|
|
10
|
+
* bound on the developer's disk.
|
|
11
|
+
* - The ingest is what stops the prune being a DATA LOSS: it carries a quiesced run's rows up to
|
|
12
|
+
* the mothership, so a run this laptop drove stays readable (by hosted teammates, and by anyone
|
|
13
|
+
* at all) after the local window passes.
|
|
14
|
+
*
|
|
15
|
+
* The returned stop is ASYNC and must be AWAITED before the store closes — each sweep's first pass
|
|
16
|
+
* is asynchronous, so an un-awaited one dies on "database is not open" and puts a spurious error
|
|
17
|
+
* line on every clean exit.
|
|
18
|
+
*/
|
|
19
|
+
export function startMothershipTelemetrySweeps(deps) {
|
|
20
|
+
const stopRetention = startLocalTelemetryRetention(deps.store, deps.retention, deps.clock, deps.log);
|
|
21
|
+
const stopIngest = startTelemetryIngest({
|
|
22
|
+
reader: deps.store.ingestReader,
|
|
23
|
+
client: deps.client,
|
|
24
|
+
clock: deps.clock,
|
|
25
|
+
log: deps.log,
|
|
26
|
+
});
|
|
27
|
+
return async () => {
|
|
28
|
+
await stopRetention();
|
|
29
|
+
await stopIngest();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=telemetrySweeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetrySweeps.js","sourceRoot":"","sources":["../src/telemetrySweeps.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAiBtE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,8BAA8B,CAC5C,IAAkC;IAElC,MAAM,aAAa,GAAG,4BAA4B,CAChD,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,GAAG,CACT,CAAA;IACD,MAAM,UAAU,GAAG,oBAAoB,CAAC;QACtC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC,CAAA;IACF,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,aAAa,EAAE,CAAA;QACrB,MAAM,UAAU,EAAE,CAAA;IACpB,CAAC,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/local-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.93.1",
|
|
4
4
|
"description": "Local-mode runtime facade for the Agent Architecture Board: the @cat-factory/node-server stack with agent jobs run as local Docker/Podman containers and GitHub accessed via a personal access token — a developer can run the whole product on their own machine.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,23 +27,23 @@
|
|
|
27
27
|
"@hono/node-server": "^2.0.12",
|
|
28
28
|
"drizzle-orm": "1.0.0-rc.4",
|
|
29
29
|
"ws": "^8.21.1",
|
|
30
|
-
"@cat-factory/agents": "0.
|
|
31
|
-
"@cat-factory/contracts": "0.
|
|
32
|
-
"@cat-factory/executor-harness": "1.
|
|
33
|
-
"@cat-factory/gitlab": "0.14.
|
|
34
|
-
"@cat-factory/integrations": "0.113.
|
|
35
|
-
"@cat-factory/kernel": "0.
|
|
36
|
-
"@cat-factory/node-server": "0.
|
|
37
|
-
"@cat-factory/orchestration": "0.
|
|
38
|
-
"@cat-factory/server": "0.
|
|
30
|
+
"@cat-factory/agents": "0.95.1",
|
|
31
|
+
"@cat-factory/contracts": "0.203.0",
|
|
32
|
+
"@cat-factory/executor-harness": "1.84.0",
|
|
33
|
+
"@cat-factory/gitlab": "0.14.15",
|
|
34
|
+
"@cat-factory/integrations": "0.113.7",
|
|
35
|
+
"@cat-factory/kernel": "0.202.0",
|
|
36
|
+
"@cat-factory/node-server": "0.145.0",
|
|
37
|
+
"@cat-factory/orchestration": "0.178.1",
|
|
38
|
+
"@cat-factory/server": "0.187.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^26.1.1",
|
|
42
42
|
"@types/ws": "^8.18.1",
|
|
43
43
|
"typescript": "7.0.2",
|
|
44
44
|
"vitest": "^4.1.10",
|
|
45
|
-
"@cat-factory/conformance": "0.16.
|
|
46
|
-
"@cat-factory/gates": "0.8.
|
|
45
|
+
"@cat-factory/conformance": "0.16.13",
|
|
46
|
+
"@cat-factory/gates": "0.8.32"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsc -b tsconfig.build.json",
|