@gobing-ai/ts-llm-jsonl-importer 0.4.42 → 0.4.43
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 +96 -9
- package/dist/jsonl-importer-dao.d.ts +31 -11
- package/dist/jsonl-importer-dao.d.ts.map +1 -1
- package/dist/jsonl-importer-dao.js +57 -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 +108 -11
- package/src/jsonl-importer-dao.ts +90 -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":"AA2BA,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,
|
|
1
|
+
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../src/importer.ts"],"names":[],"mappings":"AA2BA,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,CA0YrH"}
|
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, 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,36 @@ 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
|
+
ops.push({
|
|
232
|
+
sql: 'UPDATE history_message SET input_tokens = ?, output_tokens = ?, cache_read_tokens = ? WHERE record_hash = ?',
|
|
233
|
+
params: [
|
|
234
|
+
carrier.input,
|
|
235
|
+
carrier.output,
|
|
236
|
+
carrier.cacheRead,
|
|
237
|
+
codexLastAssistant.get(normalized.session_id),
|
|
238
|
+
],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
198
241
|
// Record row + ledger row join one batch op pair — a crash between the two
|
|
199
242
|
// writes is impossible (task 0060 F9).
|
|
200
243
|
ops.push(recordInsertOp(split.targetTable, recordHash, file, lineNumber, splitIndex, normalized, options.now));
|
|
201
244
|
ops.push(ledgerInsertOp(recordHash, resolvedSource, file, lineNumber, splitIndex, split.targetTable, options.now));
|
|
245
|
+
// 0678 R3 (codex): remember this session's latest assistant row so a
|
|
246
|
+
// following token_count meta row can attribute its usage to it.
|
|
247
|
+
if (definition.source === 'codex' &&
|
|
248
|
+
split.targetTable === 'history_message' &&
|
|
249
|
+
normalized.role === 'assistant' &&
|
|
250
|
+
typeof normalized.session_id === 'string') {
|
|
251
|
+
codexLastAssistant.set(normalized.session_id, recordHash);
|
|
252
|
+
}
|
|
202
253
|
}
|
|
203
254
|
importedRecords += 1;
|
|
204
255
|
}
|
|
@@ -229,11 +280,10 @@ export async function runJsonlImport(source, options) {
|
|
|
229
280
|
}
|
|
230
281
|
}
|
|
231
282
|
}
|
|
232
|
-
// Task 0624 R2 (claude): attach result sizes from this line's
|
|
283
|
+
// Task 0624 R2 (claude): attach native durations and result sizes from this line's
|
|
233
284
|
// tool_result blocks. Pairing is by `tool_use_id` → `call_id`
|
|
234
285
|
// (source ∘ session ∘ call), the same key the omp branch uses.
|
|
235
|
-
//
|
|
236
|
-
// guarded timestamp fallback does not extend to it (0624 Design).
|
|
286
|
+
// Missing native timing stays NULL; the timestamp fallback does not extend to claude.
|
|
237
287
|
if (definition.source === 'claude') {
|
|
238
288
|
const timing = claudeToolResultTiming(raw);
|
|
239
289
|
if (timing !== null) {
|
|
@@ -246,6 +296,9 @@ export async function runJsonlImport(source, options) {
|
|
|
246
296
|
}
|
|
247
297
|
if (pending !== undefined) {
|
|
248
298
|
ops.push(toolCallResultBytesUpdateOp(pending.recordHash, timing.resultBytes));
|
|
299
|
+
if (timing.durationMs !== undefined) {
|
|
300
|
+
ops.push(toolCallDurationUpdateOp(pending.recordHash, null, null, timing.durationMs));
|
|
301
|
+
}
|
|
249
302
|
}
|
|
250
303
|
}
|
|
251
304
|
}
|
|
@@ -258,11 +311,31 @@ export async function runJsonlImport(source, options) {
|
|
|
258
311
|
checkpointUpdates += 1;
|
|
259
312
|
}
|
|
260
313
|
}
|
|
314
|
+
// 0675 R1/R4: stamp file identity once per READ file (never for skipped ones).
|
|
315
|
+
// This is also what self-heals legacy rows whose stored identity is NULL — without
|
|
316
|
+
// it a fully-imported database never arms the short-circuit. Skipped when nothing
|
|
317
|
+
// changed since the stored checkpoint to keep no-op runs write-free. Degraded
|
|
318
|
+
// entries (pre-migration database without identity columns, 0678 fail-open) keep
|
|
319
|
+
// the old line-only write shape via checkpointUpsertOp's optional identity.
|
|
320
|
+
if (!options.dryRun && mode === 'incremental') {
|
|
321
|
+
const statAfter = await safeStat(fileSystem, file);
|
|
322
|
+
// Degraded = a STORED entry without identity (pre-migration database, 0678
|
|
323
|
+
// fail-open). No stored entry means first-ever import: stamp normally.
|
|
324
|
+
const identityDegraded = entry !== undefined && entry.size === null && entry.mtimeMs === null;
|
|
325
|
+
const identityChanged = entry !== undefined &&
|
|
326
|
+
(entry.size !== (statAfter?.size ?? null) || entry.mtimeMs !== (statAfter?.mtimeMs ?? null));
|
|
327
|
+
if (statAfter !== null && !identityDegraded && (entry === undefined || identityChanged)) {
|
|
328
|
+
await options.db.batch([
|
|
329
|
+
checkpointUpsertOp(resolvedSource, file, Math.max(lineNumber, checkpoint), options.now, {
|
|
330
|
+
size: statAfter.size,
|
|
331
|
+
mtimeMs: statAfter.mtimeMs,
|
|
332
|
+
}),
|
|
333
|
+
]);
|
|
334
|
+
checkpointUpdates += 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
261
337
|
}
|
|
262
338
|
}
|
|
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
339
|
let reconciliation;
|
|
267
340
|
if (mode === 'full') {
|
|
268
341
|
reconciliation = await reconcileFullImport(options.db, resolvedSource, desiredHashes, files, options.dryRun ?? false);
|
|
@@ -275,6 +348,7 @@ export async function runJsonlImport(source, options) {
|
|
|
275
348
|
importedRecords,
|
|
276
349
|
skippedDuplicates,
|
|
277
350
|
skippedCorruptLines,
|
|
351
|
+
skippedUnchangedFiles,
|
|
278
352
|
unknownRecords,
|
|
279
353
|
parseErrors,
|
|
280
354
|
validationErrors,
|
|
@@ -416,6 +490,19 @@ function matchesPattern(path, patterns) {
|
|
|
416
490
|
return path.endsWith(pattern.replace(/^\*/, ''));
|
|
417
491
|
});
|
|
418
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* Stat a file for its identity, returning null on any failure (missing file, stat unsupported).
|
|
495
|
+
* 0675 R2: the short-circuit fails OPEN — any null falls through to the normal read path.
|
|
496
|
+
*/
|
|
497
|
+
async function safeStat(fileSystem, file) {
|
|
498
|
+
try {
|
|
499
|
+
const stat = await fileSystem.stat(file);
|
|
500
|
+
return stat === null ? null : { size: stat.size, mtimeMs: stat.mtimeMs };
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
419
506
|
/**
|
|
420
507
|
* Yield lines from a file, streaming when the FileSystem supports it.
|
|
421
508
|
*
|
|
@@ -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
|
|
@@ -141,5 +161,5 @@ export declare function deleteCheckpointOperation(source: string, sourceFile: st
|
|
|
141
161
|
/** Build batched history, ledger, and checkpoint writes for OpenCode entries. */
|
|
142
162
|
export declare function openCodeBulkWriteOperations(entries: readonly OpenCodeQueuedEntry[], checkpointFiles: readonly string[], now: ImportOptions['now']): DbBatchOp[];
|
|
143
163
|
export type { CheckpointRow };
|
|
144
|
-
export { ETL_TABLE_DDL, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
164
|
+
export { ETL_TABLE_DDL, ensureTargetTable, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
145
165
|
//# 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
|
/**
|
|
@@ -242,11 +271,7 @@ export async function ledgerExistingHashes(db, hashes) {
|
|
|
242
271
|
}
|
|
243
272
|
async function insertRecord(db, targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now) {
|
|
244
273
|
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
|
-
}
|
|
274
|
+
await ensureTargetTable(db, targetTable);
|
|
250
275
|
await db.run(op.sql, ...op.params);
|
|
251
276
|
}
|
|
252
277
|
async function insertLedger(db, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, now) {
|
|
@@ -300,7 +325,7 @@ async function listEtlTables(db) {
|
|
|
300
325
|
* `(source, realpath)` by keeping the highest `last_imported_line`.
|
|
301
326
|
*/
|
|
302
327
|
async function normalizeCheckpointPaths(db, resolveRealPath) {
|
|
303
|
-
const rows = await db.queryAll('SELECT source, source_file, last_imported_line, updated_at FROM history_import_checkpoint');
|
|
328
|
+
const rows = await db.queryAll('SELECT source, source_file, last_imported_line, source_size, source_mtime_ms, updated_at FROM history_import_checkpoint');
|
|
304
329
|
// Collapse per (source, realPath) keeping the highest last_imported_line.
|
|
305
330
|
const collapsed = new Map();
|
|
306
331
|
for (const row of rows) {
|
|
@@ -312,14 +337,16 @@ async function normalizeCheckpointPaths(db, resolveRealPath) {
|
|
|
312
337
|
source: row.source,
|
|
313
338
|
source_file: canonical,
|
|
314
339
|
last_imported_line: row.last_imported_line,
|
|
340
|
+
source_size: row.source_size,
|
|
341
|
+
source_mtime_ms: row.source_mtime_ms,
|
|
315
342
|
updated_at: row.updated_at,
|
|
316
343
|
});
|
|
317
344
|
}
|
|
318
345
|
}
|
|
319
346
|
await db.exec('DELETE FROM history_import_checkpoint');
|
|
320
347
|
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);
|
|
348
|
+
await db.run(`INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, source_size, source_mtime_ms, updated_at)
|
|
349
|
+
VALUES (?, ?, ?, ?, ?, ?)`, row.source, row.source_file, row.last_imported_line, row.source_size, row.source_mtime_ms, row.updated_at);
|
|
323
350
|
}
|
|
324
351
|
}
|
|
325
352
|
/** Normalize `source_file` in a table that stores it as a plain column. */
|
|
@@ -499,4 +526,4 @@ export function openCodeBulkWriteOperations(entries, checkpointFiles, now) {
|
|
|
499
526
|
}
|
|
500
527
|
return operations;
|
|
501
528
|
}
|
|
502
|
-
export { ETL_TABLE_DDL, ensureTargetTables, insertLedger, insertRecord, ledgerExists, readCheckpoint, reconcileFullImport, resetCheckpoints, targetTableFor, timestamp, toolCallDurationUpdateOp, toolCallResultBytesUpdateOp, writeCheckpoint, };
|
|
529
|
+
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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAuExE,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAGhD;AA6CD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMhG;AAMD,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,UAAU,EAAE,CAkH/E;AAMD,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,UAAU,EAAE,CAmHvG;AAmBD,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,UAAU,EAAE,CA+GxG;AAoBD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,yDAAyD;IACzD,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CASrE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,mBAAmB,GAAG,IAAI,CAS5F;AAMD,wEAAwE;AACxE,MAAM,WAAW,sBAAsB;IACnC,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,wDAAwD;IACxD,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,sBAAsB,GAAG,IAAI,CA8BlG;AAMD,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,UAAU,EAAE,CA4L1G;AAMD,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,UAAU,EAAE,CAmHxG;AAMD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,UAAU,EAAE,CAiF3G;AAMD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB9F;AAiMD,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,UAAU,EAAE,CA8S7E;AAgFD,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAKxD;AAMD,gDAAgD;AAChD,eAAO,MAAM,gBAAgB,wBAAsE,CAAC;AACpG,4CAA4C;AAC5C,eAAO,MAAM,YAAY,wBAAsE,CAAC;AAChG,6CAA6C;AAC7C,eAAO,MAAM,aAAa,wBAAsE,CAAC;AACjG,+CAA+C;AAC/C,eAAO,MAAM,eAAe,wBAAsE,CAAC;AACnG,6CAA6C;AAC7C,eAAO,MAAM,aAAa,wBAAsE,CAAC;AACjG,8CAA8C;AAC9C,eAAO,MAAM,cAAc,wBAAsE,CAAC;AAClG,gDAAgD;AAChD,eAAO,MAAM,gBAAgB,wBAAsE,CAAC;AAQpG,oDAAoD;AACpD,eAAO,MAAM,aAAa,gCAAoB,CAAC;AAC/C,gDAAgD;AAChD,eAAO,MAAM,SAAS,gCAAoB,CAAC;AAC3C,iDAAiD;AACjD,eAAO,MAAM,UAAU,gCAAoB,CAAC;AAC5C,mDAAmD;AACnD,eAAO,MAAM,YAAY,gCAAoB,CAAC;AAC9C,iDAAiD;AACjD,eAAO,MAAM,UAAU,gCAAoB,CAAC;AAC5C,kDAAkD;AAClD,eAAO,MAAM,WAAW,gCAAoB,CAAC;AAC7C,oDAAoD;AACpD,eAAO,MAAM,aAAa,gCAAoB,CAAC"}
|