@gobing-ai/ts-llm-jsonl-importer 0.4.42 → 0.4.44
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 +10 -5
- package/dist/importer.d.ts.map +1 -1
- package/dist/importer.js +95 -9
- package/dist/jsonl-importer-dao.d.ts +38 -11
- package/dist/jsonl-importer-dao.d.ts.map +1 -1
- package/dist/jsonl-importer-dao.js +67 -30
- package/dist/mappers.d.ts +4 -2
- package/dist/mappers.d.ts.map +1 -1
- package/dist/mappers.js +33 -6
- package/dist/opencode-importer.d.ts.map +1 -1
- package/dist/opencode-importer.js +2 -0
- package/dist/schema-sql.d.ts +2 -5
- package/dist/schema-sql.d.ts.map +1 -1
- package/dist/schema-sql.js +5 -5
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/importer.ts +110 -11
- package/src/jsonl-importer-dao.ts +104 -28
- package/src/mappers.ts +45 -9
- package/src/opencode-importer.ts +2 -0
- package/src/schema-sql.ts +5 -5
- package/src/types.ts +2 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Generic JSONL import pipeline for AI-agent history-style files: discover files,
|
|
|
9
9
|
| Export | Purpose |
|
|
10
10
|
|--------|---------|
|
|
11
11
|
| `runJsonlImport()` | Runs discovery, parsing, validation, redaction, dedupe, and persistence. Accepts a built-in source string **or** a custom `SourceDefinition`. |
|
|
12
|
-
| `applyHistoryImportSchema()` | Installs importer-owned checkpoint, ledger, and
|
|
12
|
+
| `applyHistoryImportSchema()` | Installs importer-owned checkpoint, ledger, and typed contract tables |
|
|
13
13
|
| `SOURCE_DEFINITIONS` | Built-in source definitions |
|
|
14
14
|
| `getSourceDefinition()` | Returns one built-in definition by key (throws `HistoryImportError` for unknown keys) |
|
|
15
15
|
| `resolveSourceDefinition()` | Resolves a `string \| SourceDefinition` into a validated definition |
|
|
@@ -19,7 +19,7 @@ Generic JSONL import pipeline for AI-agent history-style files: discover files,
|
|
|
19
19
|
| `sha256()` / `stableJson()` | Stable hash helpers used by the ledger |
|
|
20
20
|
| `HISTORY_IMPORT_SCHEMA_SQL` | SQL schema string for explicit migration flows |
|
|
21
21
|
|
|
22
|
-
Built-in source keys are `claude`, `codex`, `gemini`, `pi`, `opencode`, `antigravity`, and `
|
|
22
|
+
Built-in source keys are `claude`, `codex`, `gemini`, `pi`, `opencode`, `antigravity`, `openclaw`, `omp`, `grok`, and `agy`.
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
@@ -127,13 +127,18 @@ The schema contains:
|
|
|
127
127
|
|-------|---------|
|
|
128
128
|
| `history_import_checkpoint` | Per-source/per-file last imported line |
|
|
129
129
|
| `history_import_ledger` | Stable hash ledger for dedupe and provenance |
|
|
130
|
-
| `
|
|
130
|
+
| `history_message` | Typed normalized messages from built-in forensic mappers |
|
|
131
|
+
| `history_tool_call` | Typed normalized tool calls from built-in forensic mappers |
|
|
132
|
+
| `history_etl_<source>` | Redacted payloads for generic/custom definitions; created only after an accepted row targets one |
|
|
131
133
|
|
|
132
|
-
ETL tables store
|
|
134
|
+
Generic ETL tables store normalized payload JSON plus source file, source line, split index, hash, and timestamps.
|
|
135
|
+
Schema setup, empty scans, validation failures, and dry runs do not create empty ETL tables.
|
|
136
|
+
Claude tool results copy native `toolUseResult.durationMs` or `durationSeconds` into `duration_ms`; absent timing
|
|
137
|
+
stays `NULL` and is never inferred from message timestamps.
|
|
133
138
|
|
|
134
139
|
## Split Records
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
A source can split one JSONL row into several typed or generic records. Built-in source mappers normalize supported records into `history_message` and `history_tool_call`; custom definitions may target a generic ETL table.
|
|
137
142
|
|
|
138
143
|
```ts
|
|
139
144
|
const result = await runJsonlImport('pi', { db, files: ['session.jsonl'], mode: 'full' });
|
package/dist/importer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAER,aAAa,EACb,YAAY,EAGZ,gBAAgB,EAEnB,MAAM,SAAS,CAAC;AAoFjB;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA2YrH"}
|
package/dist/importer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ambientRuntimePaths, createNodeFileSystem, resolvePath, walkDir, } from '@gobing-ai/ts-runtime';
|
|
2
2
|
import { sha256 } from './hash.js';
|
|
3
|
-
import { applyHistoryImportSchema, checkpointUpsertOp,
|
|
3
|
+
import { applyHistoryImportSchema, checkpointUpsertOp, codexUsageAttributionUpdateOp, ensureTargetTable, ledgerExistingHashes, ledgerInsertOp, loadSourceCheckpoints, reconcileFullImport, recordInsertOp, resetCheckpoints, targetTableFor, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, } from './jsonl-importer-dao.js';
|
|
4
4
|
import { claudeToolResultTiming, ompToolResultTiming, timestampToEpochMs } from './mappers.js';
|
|
5
5
|
import { redactRecord } from './redaction.js';
|
|
6
6
|
import { resolveSourceDefinition } from './sources.js';
|
|
@@ -52,7 +52,6 @@ export async function runJsonlImport(source, options) {
|
|
|
52
52
|
const resolvedSource = definition.source;
|
|
53
53
|
const fileSystem = options.fileSystem ?? createNodeFileSystem();
|
|
54
54
|
await applyHistoryImportSchema(options.db);
|
|
55
|
-
await ensureTargetTables(options.db, definition);
|
|
56
55
|
const mode = options.mode ?? 'incremental';
|
|
57
56
|
const files = await discoverFiles(definition, options.roots, options.files, fileSystem, options.paths);
|
|
58
57
|
if (mode === 'full' && !options.dryRun) {
|
|
@@ -65,15 +64,33 @@ export async function runJsonlImport(source, options) {
|
|
|
65
64
|
let skippedDuplicates = 0;
|
|
66
65
|
let unknownRecords = 0;
|
|
67
66
|
let skippedCorruptLines = 0;
|
|
67
|
+
let skippedUnchangedFiles = 0;
|
|
68
68
|
let checkpointUpdates = 0;
|
|
69
|
+
const ensuredTargetTables = new Set();
|
|
69
70
|
// Full-mode desired set (R1, task 0504): every record hash the current source produces.
|
|
70
71
|
// ReconcileFullImport diffs this against the persisted ledger to retire stale rows.
|
|
71
72
|
const desiredHashes = new Set();
|
|
72
73
|
// Task 0564 R1: tool-call rows emitted by earlier assistant lines, keyed by
|
|
73
74
|
// (source, session_id, call_id) so a later toolResult line can attach its duration.
|
|
74
75
|
const toolCallRows = new Map();
|
|
76
|
+
// 0678 R3: codex token_count events carry usage on a meta row; the numbers belong
|
|
77
|
+
// to the most recent assistant message of that session.
|
|
78
|
+
const codexLastAssistant = new Map();
|
|
79
|
+
// 0675 R5: one query per source replaces the per-file SELECT in this loop.
|
|
80
|
+
// Only incremental mode consults checkpoints; full/force-file read everything.
|
|
81
|
+
const checkpoints = mode === 'incremental' ? await loadSourceCheckpoints(options.db, resolvedSource) : undefined;
|
|
75
82
|
for (const file of files) {
|
|
76
|
-
const
|
|
83
|
+
const entry = checkpoints?.get(file);
|
|
84
|
+
const checkpoint = entry?.line ?? 0;
|
|
85
|
+
if (checkpoints !== undefined && entry !== undefined && entry.size !== null && entry.mtimeMs !== null) {
|
|
86
|
+
// 0675 R2: identity short-circuit — skip the whole file when (size, mtimeMs) both match.
|
|
87
|
+
// Fail open on any stat problem; never applies to full/force-file (checkpoints is undefined there).
|
|
88
|
+
const stat = await safeStat(fileSystem, file);
|
|
89
|
+
if (stat !== null && stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) {
|
|
90
|
+
skippedUnchangedFiles += 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
77
94
|
let lineNumber = 0;
|
|
78
95
|
for await (const rawLine of readLines(fileSystem, file)) {
|
|
79
96
|
lineNumber += 1;
|
|
@@ -177,6 +194,14 @@ export async function runJsonlImport(source, options) {
|
|
|
177
194
|
}
|
|
178
195
|
return true;
|
|
179
196
|
});
|
|
197
|
+
if (!options.dryRun) {
|
|
198
|
+
for (const entry of accepted) {
|
|
199
|
+
if (ensuredTargetTables.has(entry.split.targetTable))
|
|
200
|
+
continue;
|
|
201
|
+
await ensureTargetTable(options.db, entry.split.targetTable);
|
|
202
|
+
ensuredTargetTables.add(entry.split.targetTable);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
180
205
|
// Second pass: resolve _messageSplitIndex → message_hash, then batch.
|
|
181
206
|
const recordHashBySplitIndex = new Map(prepared.map((e) => [e.splitIndex, e.recordHash]));
|
|
182
207
|
const ops = [];
|
|
@@ -195,10 +220,35 @@ export async function runJsonlImport(source, options) {
|
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
if (!options.dryRun) {
|
|
223
|
+
// 0678 R3 (codex): a meta row carrying `_codexUsageCarrier` contributes its
|
|
224
|
+
// numbers to the latest assistant message instead of itself; with no known
|
|
225
|
+
// target the usage stays unattributed — never reattached to a wrong row.
|
|
226
|
+
const carrier = normalized._codexUsageCarrier;
|
|
227
|
+
delete normalized._codexUsageCarrier;
|
|
228
|
+
if (carrier !== undefined &&
|
|
229
|
+
typeof normalized.session_id === 'string' &&
|
|
230
|
+
codexLastAssistant.has(normalized.session_id)) {
|
|
231
|
+
const lastHash = codexLastAssistant.get(normalized.session_id);
|
|
232
|
+
if (lastHash !== undefined) {
|
|
233
|
+
ops.push(codexUsageAttributionUpdateOp(lastHash, {
|
|
234
|
+
input: carrier.input,
|
|
235
|
+
output: carrier.output,
|
|
236
|
+
cacheRead: carrier.cacheRead,
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
198
240
|
// Record row + ledger row join one batch op pair — a crash between the two
|
|
199
241
|
// writes is impossible (task 0060 F9).
|
|
200
242
|
ops.push(recordInsertOp(split.targetTable, recordHash, file, lineNumber, splitIndex, normalized, options.now));
|
|
201
243
|
ops.push(ledgerInsertOp(recordHash, resolvedSource, file, lineNumber, splitIndex, split.targetTable, options.now));
|
|
244
|
+
// 0678 R3 (codex): remember this session's latest assistant row so a
|
|
245
|
+
// following token_count meta row can attribute its usage to it.
|
|
246
|
+
if (definition.source === 'codex' &&
|
|
247
|
+
split.targetTable === 'history_message' &&
|
|
248
|
+
normalized.role === 'assistant' &&
|
|
249
|
+
typeof normalized.session_id === 'string') {
|
|
250
|
+
codexLastAssistant.set(normalized.session_id, recordHash);
|
|
251
|
+
}
|
|
202
252
|
}
|
|
203
253
|
importedRecords += 1;
|
|
204
254
|
}
|
|
@@ -229,11 +279,10 @@ export async function runJsonlImport(source, options) {
|
|
|
229
279
|
}
|
|
230
280
|
}
|
|
231
281
|
}
|
|
232
|
-
// Task 0624 R2 (claude): attach result sizes from this line's
|
|
282
|
+
// Task 0624 R2 (claude): attach native durations and result sizes from this line's
|
|
233
283
|
// tool_result blocks. Pairing is by `tool_use_id` → `call_id`
|
|
234
284
|
// (source ∘ session ∘ call), the same key the omp branch uses.
|
|
235
|
-
//
|
|
236
|
-
// guarded timestamp fallback does not extend to it (0624 Design).
|
|
285
|
+
// Missing native timing stays NULL; the timestamp fallback does not extend to claude.
|
|
237
286
|
if (definition.source === 'claude') {
|
|
238
287
|
const timing = claudeToolResultTiming(raw);
|
|
239
288
|
if (timing !== null) {
|
|
@@ -246,6 +295,9 @@ export async function runJsonlImport(source, options) {
|
|
|
246
295
|
}
|
|
247
296
|
if (pending !== undefined) {
|
|
248
297
|
ops.push(toolCallResultBytesUpdateOp(pending.recordHash, timing.resultBytes));
|
|
298
|
+
if (timing.durationMs !== undefined) {
|
|
299
|
+
ops.push(toolCallDurationUpdateOp(pending.recordHash, null, null, timing.durationMs));
|
|
300
|
+
}
|
|
249
301
|
}
|
|
250
302
|
}
|
|
251
303
|
}
|
|
@@ -258,11 +310,31 @@ export async function runJsonlImport(source, options) {
|
|
|
258
310
|
checkpointUpdates += 1;
|
|
259
311
|
}
|
|
260
312
|
}
|
|
313
|
+
// 0675 R1/R4: stamp file identity once per READ file (never for skipped ones).
|
|
314
|
+
// This is also what self-heals legacy rows whose stored identity is NULL — without
|
|
315
|
+
// it a fully-imported database never arms the short-circuit. Skipped when nothing
|
|
316
|
+
// changed since the stored checkpoint to keep no-op runs write-free. Degraded
|
|
317
|
+
// entries (pre-migration database without identity columns, 0678 fail-open) keep
|
|
318
|
+
// the old line-only write shape via checkpointUpsertOp's optional identity.
|
|
319
|
+
if (!options.dryRun && mode === 'incremental') {
|
|
320
|
+
const statAfter = await safeStat(fileSystem, file);
|
|
321
|
+
// Degraded = a STORED entry without identity (pre-migration database, 0678
|
|
322
|
+
// fail-open). No stored entry means first-ever import: stamp normally.
|
|
323
|
+
const identityDegraded = entry !== undefined && entry.size === null && entry.mtimeMs === null;
|
|
324
|
+
const identityChanged = entry !== undefined &&
|
|
325
|
+
(entry.size !== (statAfter?.size ?? null) || entry.mtimeMs !== (statAfter?.mtimeMs ?? null));
|
|
326
|
+
if (statAfter !== null && !identityDegraded && (entry === undefined || identityChanged)) {
|
|
327
|
+
await options.db.batch([
|
|
328
|
+
checkpointUpsertOp(resolvedSource, file, Math.max(lineNumber, checkpoint), options.now, {
|
|
329
|
+
size: statAfter.size,
|
|
330
|
+
mtimeMs: statAfter.mtimeMs,
|
|
331
|
+
}),
|
|
332
|
+
]);
|
|
333
|
+
checkpointUpdates += 1;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
261
336
|
}
|
|
262
337
|
}
|
|
263
|
-
// Full-mode reconciliation (R1, task 0504): diff the desired hash set against the
|
|
264
|
-
// persisted ledger and retire stale derived rows (target, tool, ledger, checkpoint) in
|
|
265
|
-
// one source-scoped batch. Dry-run computes the identical counts without mutation.
|
|
266
338
|
let reconciliation;
|
|
267
339
|
if (mode === 'full') {
|
|
268
340
|
reconciliation = await reconcileFullImport(options.db, resolvedSource, desiredHashes, files, options.dryRun ?? false);
|
|
@@ -275,6 +347,7 @@ export async function runJsonlImport(source, options) {
|
|
|
275
347
|
importedRecords,
|
|
276
348
|
skippedDuplicates,
|
|
277
349
|
skippedCorruptLines,
|
|
350
|
+
skippedUnchangedFiles,
|
|
278
351
|
unknownRecords,
|
|
279
352
|
parseErrors,
|
|
280
353
|
validationErrors,
|
|
@@ -416,6 +489,19 @@ function matchesPattern(path, patterns) {
|
|
|
416
489
|
return path.endsWith(pattern.replace(/^\*/, ''));
|
|
417
490
|
});
|
|
418
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Stat a file for its identity, returning null on any failure (missing file, stat unsupported).
|
|
494
|
+
* 0675 R2: the short-circuit fails OPEN — any null falls through to the normal read path.
|
|
495
|
+
*/
|
|
496
|
+
async function safeStat(fileSystem, file) {
|
|
497
|
+
try {
|
|
498
|
+
const stat = await fileSystem.stat(file);
|
|
499
|
+
return stat === null ? null : { size: stat.size, mtimeMs: stat.mtimeMs };
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
419
505
|
/**
|
|
420
506
|
* Yield lines from a file, streaming when the FileSystem supports it.
|
|
421
507
|
*
|
|
@@ -2,6 +2,14 @@ import type { DbAdapter, DbBatchOp } from '@gobing-ai/ts-db';
|
|
|
2
2
|
import type { ImportOptions, JsonObject, ReconcileSummary, SourceDefinition } from './types';
|
|
3
3
|
interface CheckpointRow {
|
|
4
4
|
readonly last_imported_line: number;
|
|
5
|
+
readonly source_size?: number | null;
|
|
6
|
+
readonly source_mtime_ms?: number | null;
|
|
7
|
+
}
|
|
8
|
+
/** File identity + line for one checkpoint row (0675 R1/R5). */
|
|
9
|
+
export interface SourceCheckpoint {
|
|
10
|
+
readonly line: number;
|
|
11
|
+
readonly size: number | null;
|
|
12
|
+
readonly mtimeMs: number | null;
|
|
5
13
|
}
|
|
6
14
|
declare function targetTableFor(table: string): string;
|
|
7
15
|
declare function timestamp(now: ImportOptions['now']): string;
|
|
@@ -9,20 +17,19 @@ declare function ETL_TABLE_DDL(table: string): string;
|
|
|
9
17
|
/**
|
|
10
18
|
* Apply importer-owned schema to the target database.
|
|
11
19
|
*
|
|
12
|
-
* Creates the checkpoint, ledger, and typed contract tables from the static SQL
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* tables exist" for callers who apply the schema and then raw-insert without a prior import.
|
|
16
|
-
* Adding a new built-in source to {@link SOURCE_DEFINITIONS} is now the only edit needed.
|
|
20
|
+
* Creates the checkpoint, ledger, and typed contract tables from the static SQL.
|
|
21
|
+
* Generic ETL tables are created lazily with the first accepted row, so applying
|
|
22
|
+
* the schema or scanning an empty source never leaves vestigial empty tables.
|
|
17
23
|
*/
|
|
18
24
|
export declare function applyHistoryImportSchema(db: ImportOptions['db']): Promise<void>;
|
|
25
|
+
/** Ensure one accepted record's target exists; typed targets come from the static schema. */
|
|
26
|
+
declare function ensureTargetTable(db: ImportOptions['db'], targetTable: string): Promise<void>;
|
|
19
27
|
/**
|
|
20
28
|
* Ensure the ETL table(s) for a source definition exist.
|
|
21
29
|
*
|
|
22
30
|
* WHY: the static {@link HISTORY_IMPORT_SCHEMA_SQL} creates only the checkpoint, ledger, and
|
|
23
|
-
* typed contract tables.
|
|
24
|
-
*
|
|
25
|
-
* with a `splitConfig.targetTable` override also need their target table(s) created on demand. The
|
|
31
|
+
* typed contract tables. Callers that explicitly request a definition's generic targets use this
|
|
32
|
+
* helper; the import pipeline instead creates only targets reached by accepted rows. The
|
|
26
33
|
* table name is already gated by
|
|
27
34
|
* {@link VALID_TABLE_NAME} in {@link validateSourceDefinition} / {@link targetTableFor},
|
|
28
35
|
* so it is safe to interpolate into DDL. `CREATE TABLE IF NOT EXISTS` is
|
|
@@ -30,6 +37,16 @@ export declare function applyHistoryImportSchema(db: ImportOptions['db']): Promi
|
|
|
30
37
|
*/
|
|
31
38
|
declare function ensureTargetTables(db: ImportOptions['db'], definition: SourceDefinition): Promise<void>;
|
|
32
39
|
declare function readCheckpoint(db: ImportOptions['db'], source: string, sourceFile: string): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* One query per source (0675 R5): all checkpoint rows for `source` keyed by file,
|
|
42
|
+
* replacing the per-file SELECT in the import loop.
|
|
43
|
+
*
|
|
44
|
+
* Fails open when the identity columns are missing (0678): a database created through a
|
|
45
|
+
* migration path that has not yet applied 0675's ALTERs would otherwise hard-fail every
|
|
46
|
+
* import. Line-level behavior degrades to pre-identity semantics — the short-circuit
|
|
47
|
+
* and identity stamping no-op until the columns exist.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadSourceCheckpoints(db: ImportOptions['db'], source: string): Promise<Map<string, SourceCheckpoint>>;
|
|
33
50
|
declare function resetCheckpoints(db: ImportOptions['db'], source: string, files: readonly string[]): Promise<void>;
|
|
34
51
|
declare function writeCheckpoint(db: ImportOptions['db'], source: string, sourceFile: string, line: number, now: ImportOptions['now']): Promise<void>;
|
|
35
52
|
declare function ledgerExists(db: ImportOptions['db'], recordHash: string): Promise<boolean>;
|
|
@@ -37,13 +54,16 @@ declare function ledgerExists(db: ImportOptions['db'], recordHash: string): Prom
|
|
|
37
54
|
* INSERT op for one accepted record row (task 0060 F9). Pure builder so the
|
|
38
55
|
* per-record batch path and the single-write `insertRecord` can never drift.
|
|
39
56
|
* Typed tables validate unknown keys here; the caller must have ensured the
|
|
40
|
-
* target table exists (
|
|
57
|
+
* target table exists (the import loop ensures accepted targets before batching).
|
|
41
58
|
*/
|
|
42
59
|
export declare function recordInsertOp(targetTable: string, recordHash: string, sourceFile: string, sourceLine: number, splitIndex: number, payload: JsonObject, now: ImportOptions['now']): DbBatchOp;
|
|
43
60
|
/** INSERT op for one ledger row (task 0060 F9). */
|
|
44
61
|
export declare function ledgerInsertOp(recordHash: string, source: string, sourceFile: string, sourceLine: number, splitIndex: number, targetTable: string, now: ImportOptions['now']): DbBatchOp;
|
|
45
62
|
/** UPSERT op for one checkpoint row (task 0060 F9: checkpoint joins the record batch). */
|
|
46
|
-
export declare function checkpointUpsertOp(source: string, sourceFile: string, line: number, now: ImportOptions['now']
|
|
63
|
+
export declare function checkpointUpsertOp(source: string, sourceFile: string, line: number, now: ImportOptions['now'], identity?: {
|
|
64
|
+
readonly size: number | null;
|
|
65
|
+
readonly mtimeMs: number | null;
|
|
66
|
+
}): DbBatchOp;
|
|
47
67
|
/**
|
|
48
68
|
* Targeted UPDATE op attaching a tool duration to a previously-inserted
|
|
49
69
|
* `history_tool_call` row (task 0564 R1). Keyed by `record_hash` (PK), never an
|
|
@@ -59,6 +79,13 @@ declare function toolCallDurationUpdateOp(recordHash: string, startedAt: string
|
|
|
59
79
|
* `history_tool_call` row (task 0624 R2). Keyed by `record_hash` (PK). Idempotent.
|
|
60
80
|
*/
|
|
61
81
|
declare function toolCallResultBytesUpdateOp(recordHash: string, resultBytes: number): DbBatchOp;
|
|
82
|
+
/** Targeted UPDATE op attributing a codex usage-carrier row's token counts to the
|
|
83
|
+
* latest assistant message of its session (task 0678 R3). Keyed by `record_hash` (PK). */
|
|
84
|
+
export declare function codexUsageAttributionUpdateOp(recordHash: string, tokens: {
|
|
85
|
+
readonly input: number | null;
|
|
86
|
+
readonly output: number | null;
|
|
87
|
+
readonly cacheRead: number | null;
|
|
88
|
+
}): DbBatchOp;
|
|
62
89
|
/**
|
|
63
90
|
* Query which of the given hashes already exist in the ledger, in chunks of at
|
|
64
91
|
* most 200 per `IN (...)` (task 0060 F9 — replaces the per-record SELECT loop).
|
|
@@ -141,5 +168,5 @@ export declare function deleteCheckpointOperation(source: string, sourceFile: st
|
|
|
141
168
|
/** Build batched history, ledger, and checkpoint writes for OpenCode entries. */
|
|
142
169
|
export declare function openCodeBulkWriteOperations(entries: readonly OpenCodeQueuedEntry[], checkpointFiles: readonly string[], now: ImportOptions['now']): DbBatchOp[];
|
|
143
170
|
export type { CheckpointRow };
|
|
144
|
-
export { ETL_TABLE_DDL, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
171
|
+
export { ETL_TABLE_DDL, ensureTargetTable, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
145
172
|
//# sourceMappingURL=jsonl-importer-dao.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonl-importer-dao.d.ts","sourceRoot":"","sources":["../src/jsonl-importer-dao.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7D,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE7F,UAAU,aAAa;IACnB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonl-importer-dao.d.ts","sourceRoot":"","sources":["../src/jsonl-importer-dao.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HistoryImportError } from './errors.js';
|
|
2
2
|
import { HISTORY_IMPORT_SCHEMA_SQL } from './schema-sql.js';
|
|
3
|
-
import {
|
|
3
|
+
import { VALID_TABLE_NAME } from './sources.js';
|
|
4
4
|
/** Column allowlist per typed contract table; order is the INSERT column order. */
|
|
5
5
|
const TYPED_TABLE_COLUMNS = {
|
|
6
6
|
history_message: [
|
|
@@ -75,11 +75,9 @@ function ETL_TABLE_DDL(table) {
|
|
|
75
75
|
/**
|
|
76
76
|
* Apply importer-owned schema to the target database.
|
|
77
77
|
*
|
|
78
|
-
* Creates the checkpoint, ledger, and typed contract tables from the static SQL
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* tables exist" for callers who apply the schema and then raw-insert without a prior import.
|
|
82
|
-
* Adding a new built-in source to {@link SOURCE_DEFINITIONS} is now the only edit needed.
|
|
78
|
+
* Creates the checkpoint, ledger, and typed contract tables from the static SQL.
|
|
79
|
+
* Generic ETL tables are created lazily with the first accepted row, so applying
|
|
80
|
+
* the schema or scanning an empty source never leaves vestigial empty tables.
|
|
83
81
|
*/
|
|
84
82
|
export async function applyHistoryImportSchema(db) {
|
|
85
83
|
for (const statement of HISTORY_IMPORT_SCHEMA_SQL.split(';')) {
|
|
@@ -88,17 +86,19 @@ export async function applyHistoryImportSchema(db) {
|
|
|
88
86
|
await db.exec(sql);
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
}
|
|
90
|
+
/** Ensure one accepted record's target exists; typed targets come from the static schema. */
|
|
91
|
+
async function ensureTargetTable(db, targetTable) {
|
|
92
|
+
const table = targetTableFor(targetTable);
|
|
93
|
+
if (TYPED_TABLE_COLUMNS[table] === undefined)
|
|
94
|
+
await db.exec(ETL_TABLE_DDL(table));
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
96
97
|
* Ensure the ETL table(s) for a source definition exist.
|
|
97
98
|
*
|
|
98
99
|
* WHY: the static {@link HISTORY_IMPORT_SCHEMA_SQL} creates only the checkpoint, ledger, and
|
|
99
|
-
* typed contract tables.
|
|
100
|
-
*
|
|
101
|
-
* with a `splitConfig.targetTable` override also need their target table(s) created on demand. The
|
|
100
|
+
* typed contract tables. Callers that explicitly request a definition's generic targets use this
|
|
101
|
+
* helper; the import pipeline instead creates only targets reached by accepted rows. The
|
|
102
102
|
* table name is already gated by
|
|
103
103
|
* {@link VALID_TABLE_NAME} in {@link validateSourceDefinition} / {@link targetTableFor},
|
|
104
104
|
* so it is safe to interpolate into DDL. `CREATE TABLE IF NOT EXISTS` is
|
|
@@ -110,16 +110,43 @@ async function ensureTargetTables(db, definition) {
|
|
|
110
110
|
tables.add(targetTableFor(definition.splitConfig.targetTable));
|
|
111
111
|
}
|
|
112
112
|
for (const table of tables) {
|
|
113
|
-
|
|
114
|
-
if (TYPED_TABLE_COLUMNS[table] !== undefined)
|
|
115
|
-
continue;
|
|
116
|
-
await db.exec(ETL_TABLE_DDL(table));
|
|
113
|
+
await ensureTargetTable(db, table);
|
|
117
114
|
}
|
|
118
115
|
}
|
|
119
116
|
async function readCheckpoint(db, source, sourceFile) {
|
|
120
117
|
const row = await db.queryFirst('SELECT last_imported_line FROM history_import_checkpoint WHERE source = ? AND source_file = ?', source, sourceFile);
|
|
121
118
|
return row?.last_imported_line ?? 0;
|
|
122
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* One query per source (0675 R5): all checkpoint rows for `source` keyed by file,
|
|
122
|
+
* replacing the per-file SELECT in the import loop.
|
|
123
|
+
*
|
|
124
|
+
* Fails open when the identity columns are missing (0678): a database created through a
|
|
125
|
+
* migration path that has not yet applied 0675's ALTERs would otherwise hard-fail every
|
|
126
|
+
* import. Line-level behavior degrades to pre-identity semantics — the short-circuit
|
|
127
|
+
* and identity stamping no-op until the columns exist.
|
|
128
|
+
*/
|
|
129
|
+
export async function loadSourceCheckpoints(db, source) {
|
|
130
|
+
let rows;
|
|
131
|
+
try {
|
|
132
|
+
rows = await db.queryAll('SELECT source_file, last_imported_line, source_size, source_mtime_ms FROM history_import_checkpoint WHERE source = ?', source);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
if (!/no such column|source_size|source_mtime_ms/i.test(err.message))
|
|
136
|
+
throw err;
|
|
137
|
+
rows = (await db.queryAll('SELECT source_file, last_imported_line FROM history_import_checkpoint WHERE source = ?', source));
|
|
138
|
+
return new Map(rows.map((row) => [row.source_file, { line: row.last_imported_line, size: null, mtimeMs: null }]));
|
|
139
|
+
}
|
|
140
|
+
const map = new Map();
|
|
141
|
+
for (const row of rows) {
|
|
142
|
+
map.set(row.source_file, {
|
|
143
|
+
line: row.last_imported_line,
|
|
144
|
+
size: row.source_size ?? null,
|
|
145
|
+
mtimeMs: row.source_mtime_ms ?? null,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return map;
|
|
149
|
+
}
|
|
123
150
|
async function resetCheckpoints(db, source, files) {
|
|
124
151
|
for (const file of files) {
|
|
125
152
|
await db.run('DELETE FROM history_import_checkpoint WHERE source = ? AND source_file = ?', source, file);
|
|
@@ -140,7 +167,7 @@ async function ledgerExists(db, recordHash) {
|
|
|
140
167
|
* INSERT op for one accepted record row (task 0060 F9). Pure builder so the
|
|
141
168
|
* per-record batch path and the single-write `insertRecord` can never drift.
|
|
142
169
|
* Typed tables validate unknown keys here; the caller must have ensured the
|
|
143
|
-
* target table exists (
|
|
170
|
+
* target table exists (the import loop ensures accepted targets before batching).
|
|
144
171
|
*/
|
|
145
172
|
export function recordInsertOp(targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now) {
|
|
146
173
|
const table = targetTableFor(targetTable);
|
|
@@ -186,14 +213,16 @@ export function ledgerInsertOp(recordHash, source, sourceFile, sourceLine, split
|
|
|
186
213
|
};
|
|
187
214
|
}
|
|
188
215
|
/** UPSERT op for one checkpoint row (task 0060 F9: checkpoint joins the record batch). */
|
|
189
|
-
export function checkpointUpsertOp(source, sourceFile, line, now) {
|
|
216
|
+
export function checkpointUpsertOp(source, sourceFile, line, now, identity) {
|
|
190
217
|
return {
|
|
191
|
-
sql: `INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, updated_at)
|
|
192
|
-
VALUES (?, ?, ?, ?)
|
|
218
|
+
sql: `INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, source_size, source_mtime_ms, updated_at)
|
|
219
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
193
220
|
ON CONFLICT(source, source_file) DO UPDATE SET
|
|
194
221
|
last_imported_line = excluded.last_imported_line,
|
|
222
|
+
source_size = excluded.source_size,
|
|
223
|
+
source_mtime_ms = excluded.source_mtime_ms,
|
|
195
224
|
updated_at = excluded.updated_at`,
|
|
196
|
-
params: [source, sourceFile, line, timestamp(now)],
|
|
225
|
+
params: [source, sourceFile, line, identity?.size ?? null, identity?.mtimeMs ?? null, timestamp(now)],
|
|
197
226
|
};
|
|
198
227
|
}
|
|
199
228
|
/**
|
|
@@ -225,6 +254,16 @@ function toolCallResultBytesUpdateOp(recordHash, resultBytes) {
|
|
|
225
254
|
params: [resultBytes, recordHash],
|
|
226
255
|
};
|
|
227
256
|
}
|
|
257
|
+
/** Targeted UPDATE op attributing a codex usage-carrier row's token counts to the
|
|
258
|
+
* latest assistant message of its session (task 0678 R3). Keyed by `record_hash` (PK). */
|
|
259
|
+
export function codexUsageAttributionUpdateOp(recordHash, tokens) {
|
|
260
|
+
return {
|
|
261
|
+
sql: `UPDATE history_message
|
|
262
|
+
SET input_tokens = ?, output_tokens = ?, cache_read_tokens = ?
|
|
263
|
+
WHERE record_hash = ?`,
|
|
264
|
+
params: [tokens.input, tokens.output, tokens.cacheRead, recordHash],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
228
267
|
/**
|
|
229
268
|
* Query which of the given hashes already exist in the ledger, in chunks of at
|
|
230
269
|
* most 200 per `IN (...)` (task 0060 F9 — replaces the per-record SELECT loop).
|
|
@@ -242,11 +281,7 @@ export async function ledgerExistingHashes(db, hashes) {
|
|
|
242
281
|
}
|
|
243
282
|
async function insertRecord(db, targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now) {
|
|
244
283
|
const op = recordInsertOp(targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now);
|
|
245
|
-
|
|
246
|
-
// Custom ETL tables are created on demand; `CREATE TABLE IF NOT EXISTS` is idempotent,
|
|
247
|
-
// so there is no process-global cache to go stale across adapter instances (task 0060 F8).
|
|
248
|
-
await db.exec(ETL_TABLE_DDL(targetTableFor(targetTable)));
|
|
249
|
-
}
|
|
284
|
+
await ensureTargetTable(db, targetTable);
|
|
250
285
|
await db.run(op.sql, ...op.params);
|
|
251
286
|
}
|
|
252
287
|
async function insertLedger(db, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, now) {
|
|
@@ -300,7 +335,7 @@ async function listEtlTables(db) {
|
|
|
300
335
|
* `(source, realpath)` by keeping the highest `last_imported_line`.
|
|
301
336
|
*/
|
|
302
337
|
async function normalizeCheckpointPaths(db, resolveRealPath) {
|
|
303
|
-
const rows = await db.queryAll('SELECT source, source_file, last_imported_line, updated_at FROM history_import_checkpoint');
|
|
338
|
+
const rows = await db.queryAll('SELECT source, source_file, last_imported_line, source_size, source_mtime_ms, updated_at FROM history_import_checkpoint');
|
|
304
339
|
// Collapse per (source, realPath) keeping the highest last_imported_line.
|
|
305
340
|
const collapsed = new Map();
|
|
306
341
|
for (const row of rows) {
|
|
@@ -312,14 +347,16 @@ async function normalizeCheckpointPaths(db, resolveRealPath) {
|
|
|
312
347
|
source: row.source,
|
|
313
348
|
source_file: canonical,
|
|
314
349
|
last_imported_line: row.last_imported_line,
|
|
350
|
+
source_size: row.source_size,
|
|
351
|
+
source_mtime_ms: row.source_mtime_ms,
|
|
315
352
|
updated_at: row.updated_at,
|
|
316
353
|
});
|
|
317
354
|
}
|
|
318
355
|
}
|
|
319
356
|
await db.exec('DELETE FROM history_import_checkpoint');
|
|
320
357
|
for (const row of collapsed.values()) {
|
|
321
|
-
await db.run(`INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, updated_at)
|
|
322
|
-
VALUES (?, ?, ?, ?)`, row.source, row.source_file, row.last_imported_line, row.updated_at);
|
|
358
|
+
await db.run(`INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, source_size, source_mtime_ms, updated_at)
|
|
359
|
+
VALUES (?, ?, ?, ?, ?, ?)`, row.source, row.source_file, row.last_imported_line, row.source_size, row.source_mtime_ms, row.updated_at);
|
|
323
360
|
}
|
|
324
361
|
}
|
|
325
362
|
/** Normalize `source_file` in a table that stores it as a plain column. */
|
|
@@ -499,4 +536,4 @@ export function openCodeBulkWriteOperations(entries, checkpointFiles, now) {
|
|
|
499
536
|
}
|
|
500
537
|
return operations;
|
|
501
538
|
}
|
|
502
|
-
export { ETL_TABLE_DDL, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
539
|
+
export { ETL_TABLE_DDL, ensureTargetTable, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
package/dist/mappers.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface ClaudeToolResultTiming {
|
|
|
39
39
|
toolCallId: string;
|
|
40
40
|
/** Message timestamp as epoch millis, when parseable. */
|
|
41
41
|
timestampMs: number | undefined;
|
|
42
|
+
/** Native tool duration, when Claude Code emits one. */
|
|
43
|
+
durationMs: number | undefined;
|
|
42
44
|
/** Serialized size in bytes of the tool result payload. */
|
|
43
45
|
resultBytes: number;
|
|
44
46
|
}
|
|
@@ -48,8 +50,8 @@ export interface ClaudeToolResultTiming {
|
|
|
48
50
|
* `type: "user"` lines whose `message.content` holds `{type: "tool_result",
|
|
49
51
|
* tool_use_id, content}` blocks; the top-level `toolUseResult` carries the raw
|
|
50
52
|
* envelope. `resultBytes` prefers the model-visible block content and falls
|
|
51
|
-
* back to `toolUseResult`; both are null-safe.
|
|
52
|
-
*
|
|
53
|
+
* back to `toolUseResult`; both are null-safe. Some tool results carry a native
|
|
54
|
+
* `durationMs` or `durationSeconds`; absent timing stays unmeasured.
|
|
53
55
|
*/
|
|
54
56
|
export declare function claudeToolResultTiming(raw: Record<string, unknown>): ClaudeToolResultTiming | null;
|
|
55
57
|
/** Map a Codex JSONL record into one or more ETL split entries. */
|
package/dist/mappers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mappers.d.ts","sourceRoot":"","sources":["../src/mappers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mappers.d.ts","sourceRoot":"","sources":["../src/mappers.ts"],"names":[],"mappings":"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"}
|