@gobing-ai/ts-llm-jsonl-importer 0.4.52 → 0.4.54

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/index.d.ts CHANGED
@@ -2,10 +2,10 @@ export { HistoryImportError } from './errors';
2
2
  export { sha256, stableJson } from './hash';
3
3
  export { runJsonlImport } from './importer';
4
4
  export { applyHistoryImportSchema, normalizeSourceFilePaths } from './jsonl-importer-dao';
5
- export { sessionIdFromSourcePath } from './mappers';
5
+ export { canonicalizeSkillName, extractSkillCalls, type SkillCallIdentity, sessionIdFromSourcePath, skillCallEntry, } from './mappers';
6
6
  export { type OpenCodeImportOptions, runOpenCodeImport } from './opencode-importer';
7
7
  export { DEFAULT_REDACTION_RULES, redactRecord, redactValue } from './redaction';
8
8
  export { HISTORY_IMPORT_SCHEMA_SQL } from './schema-sql';
9
9
  export { getSourceDefinition, resolveSourceDefinition, SOURCE_DEFINITIONS, VALID_TABLE_NAME, validateSourceDefinition, } from './sources';
10
- export type { FieldTransform, ImportIssue, ImportMode, ImportOptions, ImportResult, JsonObject, LlmJsonlSource, ReconcileSummary, RedactionRule, SkillCall, SourceDefinition, SplitConfig, SplitEntry, TransformContext, } from './types';
10
+ export type { FieldTransform, ImportIssue, ImportMode, ImportOptions, ImportResult, JsonObject, LlmJsonlSource, ReconcileSummary, RedactionRule, SkillCall, SkillCallSplitRecord, SourceDefinition, SplitConfig, SplitEntry, TransformContext, } from './types';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EACH,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,GAC3B,MAAM,WAAW,CAAC;AACnB,YAAY,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,gBAAgB,GACnB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACH,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,cAAc,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EACH,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,GAC3B,MAAM,WAAW,CAAC;AACnB,YAAY,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,gBAAgB,GACnB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export { HistoryImportError } from './errors.js';
2
2
  export { sha256, stableJson } from './hash.js';
3
3
  export { runJsonlImport } from './importer.js';
4
4
  export { applyHistoryImportSchema, normalizeSourceFilePaths } from './jsonl-importer-dao.js';
5
- export { sessionIdFromSourcePath } from './mappers.js';
5
+ export { canonicalizeSkillName, extractSkillCalls, sessionIdFromSourcePath, skillCallEntry, } from './mappers.js';
6
6
  export { runOpenCodeImport } from './opencode-importer.js';
7
7
  export { DEFAULT_REDACTION_RULES, redactRecord, redactValue } from './redaction.js';
8
8
  export { HISTORY_IMPORT_SCHEMA_SQL } from './schema-sql.js';
@@ -149,7 +149,7 @@ export interface OpenCodeExistingEntry {
149
149
  }
150
150
  /** Normalized OpenCode entry awaiting a history-table write. */
151
151
  export interface OpenCodeQueuedEntry {
152
- targetTable: 'history_message' | 'history_tool_call';
152
+ targetTable: 'history_message' | 'history_tool_call' | 'history_skill_call';
153
153
  splitIndex: number;
154
154
  record: JsonObject;
155
155
  recordHash: string;
@@ -1 +1 @@
1
- {"version":3,"file":"jsonl-importer-dao.d.ts","sourceRoot":"","sources":["../src/jsonl-importer-dao.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jsonl-importer-dao.d.ts","sourceRoot":"","sources":["../src/jsonl-importer-dao.ts"],"names":[],"mappings":"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"}
@@ -502,7 +502,7 @@ export function deleteCheckpointOperation(source, sourceFile) {
502
502
  export function openCodeBulkWriteOperations(entries, checkpointFiles, now) {
503
503
  const importedAt = timestamp(now);
504
504
  const operations = [];
505
- for (const table of ['history_message', 'history_tool_call']) {
505
+ for (const table of ['history_message', 'history_tool_call', 'history_skill_call']) {
506
506
  const tableEntries = entries.filter((entry) => entry.targetTable === table);
507
507
  for (let offset = 0; offset < tableEntries.length; offset += 100) {
508
508
  const chunk = tableEntries.slice(offset, offset + 100);
package/dist/mappers.d.ts CHANGED
@@ -1,5 +1,36 @@
1
1
  import { z } from 'zod';
2
- import type { JsonObject, SplitEntry, TransformContext } from './types';
2
+ import type { JsonObject, SkillCallSplitRecord, SplitEntry, TransformContext } from './types';
3
+ /**
4
+ * Canonicalize a harness skill name: `sp-dev-run` / `$sp-dev-run` / `/skill:sp-dev-run` →
5
+ * `sp:dev-run`; `rd3-*` → `rd3:*`. Only the frozen harness package set is rewritten — an
6
+ * unqualified name like `code-review` is kept verbatim (exact structural match, 0736 R4).
7
+ */
8
+ export declare function canonicalizeSkillName(raw: string): string;
9
+ /** Identity fields a split already computed for its parent message (skill rows join it). */
10
+ export interface SkillCallIdentity {
11
+ readonly sessionId: string;
12
+ readonly seq: number;
13
+ /** Split index of the parent message entry in the same split batch (_messageSplitIndex). */
14
+ readonly messageSplitIndex: number;
15
+ }
16
+ /**
17
+ * Detect skill-load events in one raw source record (0736 R1/R2). Dispatches on the source in
18
+ * the transform context; unknown sources return nothing. Detection signatures per source are
19
+ * the verified ones from the storm report §10.3:
20
+ *
21
+ * - L1 native load tool is authoritative (claude/omp `Skill`, agy `view_file` skill reads,
22
+ * grok `read_file` on SKILL.md, opencode native `skill`).
23
+ * - Sources with no L1 trigger on their structural signal: pi's `<skill name= location=>`
24
+ * wrapper, codex's `<skill><name>/<path>` block, gemini's L0 harness prefix.
25
+ * - L0/L2 never trigger for agents that have an L1 (false-positive suppression, 0736 R4).
26
+ */
27
+ export declare function extractSkillCalls(raw: JsonObject, context: TransformContext | undefined, identity: SkillCallIdentity): readonly SkillCallSplitRecord[];
28
+ /**
29
+ * Route one skill-load row to `history_skill_call` exactly like tool calls route to
30
+ * `history_tool_call`: a SplitEntry whose record the importer normalizes, hashes, dedups,
31
+ * and inserts through the typed-column path.
32
+ */
33
+ export declare function skillCallEntry(record: SkillCallSplitRecord): SplitEntry;
3
34
  /** Compute args_digest: sha256 of stable-jsonified, key-sorted, redacted args. */
4
35
  export declare function argsDigest(args: unknown): string;
5
36
  /**
@@ -9,7 +40,7 @@ export declare function argsDigest(args: unknown): string;
9
40
  */
10
41
  export declare function maybeArgsRaw(_source: string, toolName: string, args: unknown): string | undefined;
11
42
  /** Map a Claude Code JSONL record into one or more ETL split entries. */
12
- export declare function claudeSplit(raw: Record<string, unknown>): readonly SplitEntry[];
43
+ export declare function claudeSplit(raw: Record<string, unknown>, context?: TransformContext): readonly SplitEntry[];
13
44
  /** Map a Pi JSONL record into one or more ETL split entries. */
14
45
  export declare function piSplit(raw: Record<string, unknown>, context?: TransformContext): readonly SplitEntry[];
15
46
  /** Map an OMP JSONL record into one or more ETL split entries. */
@@ -70,7 +101,7 @@ export declare function geminiSplit(raw: Record<string, unknown>, context?: Tran
70
101
  */
71
102
  export declare function sessionIdFromSourcePath(source: string, sourceFile: string): string | undefined;
72
103
  /** Map a Grok JSONL record into one or more ETL split entries. */
73
- export declare function grokSplit(raw: Record<string, unknown>): readonly SplitEntry[];
104
+ export declare function grokSplit(raw: Record<string, unknown>, context?: TransformContext): readonly SplitEntry[];
74
105
  /** Compute a stable field-shape key from a JSON object: sorted, lowercased, +-joined top-level keys. */
75
106
  export declare function stableFieldShape(raw: JsonObject): string;
76
107
  /** Identity field map for the Claude mapper. */
@@ -1 +1 @@
1
- {"version":3,"file":"mappers.d.ts","sourceRoot":"","sources":["../src/mappers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mappers.d.ts","sourceRoot":"","sources":["../src/mappers.ts"],"names":[],"mappings":"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"}
package/dist/mappers.js CHANGED
@@ -45,6 +45,22 @@ const TOOL_CALL_MAPPER_KEYS = [
45
45
  'result_bytes',
46
46
  'error_text',
47
47
  ];
48
+ /** Columns the mapper may produce for history_skill_call (0736). */
49
+ const SKILL_CALL_MAPPER_KEYS = [
50
+ '_messageSplitIndex',
51
+ 'session_id',
52
+ 'seq',
53
+ 'skill_name',
54
+ 'invocation_kind',
55
+ 'skill_path',
56
+ 'args_raw',
57
+ 'args_digest',
58
+ 'call_id',
59
+ 'status',
60
+ 'started_at',
61
+ 'completed_at',
62
+ 'duration_ms',
63
+ ];
48
64
  /** Build an identity fieldMap for the given column list. */
49
65
  function identityFieldMap(keys) {
50
66
  const map = {};
@@ -54,6 +70,251 @@ function identityFieldMap(keys) {
54
70
  return map;
55
71
  }
56
72
  // ---------------------------------------------------------------------------
73
+ // Skill-load extraction (0736)
74
+ // ---------------------------------------------------------------------------
75
+ /** Harness skill packages whose dialect spelling swaps ':' for '-' (storm report §10.1). */
76
+ const HARNESS_SKILL_PACKAGES = new Set(['sp', 'rd3']);
77
+ /**
78
+ * Canonicalize a harness skill name: `sp-dev-run` / `$sp-dev-run` / `/skill:sp-dev-run` →
79
+ * `sp:dev-run`; `rd3-*` → `rd3:*`. Only the frozen harness package set is rewritten — an
80
+ * unqualified name like `code-review` is kept verbatim (exact structural match, 0736 R4).
81
+ */
82
+ export function canonicalizeSkillName(raw) {
83
+ let name = raw.trim();
84
+ if (name.startsWith('/') || name.startsWith('$'))
85
+ name = name.slice(1);
86
+ if (name.startsWith('skill:'))
87
+ name = name.slice('skill:'.length);
88
+ const canonical = name.match(/^([a-z][a-z0-9]*):([a-z0-9-]+)$/);
89
+ if (canonical !== null && HARNESS_SKILL_PACKAGES.has(canonical[1] ?? ''))
90
+ return name;
91
+ const dialect = name.match(/^([a-z][a-z0-9]*)-([a-z0-9-]+)$/);
92
+ if (dialect !== null && HARNESS_SKILL_PACKAGES.has(dialect[1] ?? ''))
93
+ return `${dialect[1]}:${dialect[2]}`;
94
+ return name;
95
+ }
96
+ /**
97
+ * Detect skill-load events in one raw source record (0736 R1/R2). Dispatches on the source in
98
+ * the transform context; unknown sources return nothing. Detection signatures per source are
99
+ * the verified ones from the storm report §10.3:
100
+ *
101
+ * - L1 native load tool is authoritative (claude/omp `Skill`, agy `view_file` skill reads,
102
+ * grok `read_file` on SKILL.md, opencode native `skill`).
103
+ * - Sources with no L1 trigger on their structural signal: pi's `<skill name= location=>`
104
+ * wrapper, codex's `<skill><name>/<path>` block, gemini's L0 harness prefix.
105
+ * - L0/L2 never trigger for agents that have an L1 (false-positive suppression, 0736 R4).
106
+ */
107
+ export function extractSkillCalls(raw, context, identity) {
108
+ switch (context?.source) {
109
+ case 'claude':
110
+ return detectClaudeSkillCalls(raw, identity);
111
+ case 'pi':
112
+ return detectPiSkillCalls(raw, identity);
113
+ case 'omp':
114
+ return detectOmpSkillCalls(raw, identity);
115
+ case 'codex':
116
+ return detectCodexSkillCalls(raw, identity);
117
+ case 'agy':
118
+ return detectAgySkillCalls(raw, identity);
119
+ case 'gemini':
120
+ return detectGeminiSkillCalls(raw, identity);
121
+ case 'grok':
122
+ return detectGrokSkillCalls(raw, identity);
123
+ default:
124
+ return [];
125
+ }
126
+ }
127
+ /**
128
+ * Route one skill-load row to `history_skill_call` exactly like tool calls route to
129
+ * `history_tool_call`: a SplitEntry whose record the importer normalizes, hashes, dedups,
130
+ * and inserts through the typed-column path.
131
+ */
132
+ export function skillCallEntry(record) {
133
+ // The split record's keys are exactly the history_skill_call typed columns; the index
134
+ // signature is supplied at this boundary (runtime shape is JSON-compatible by construction).
135
+ return { targetTable: 'history_skill_call', record: record };
136
+ }
137
+ /** Build a fully-populated skill row: every optional column set explicitly for hash stability. */
138
+ function skillRecord(identity, skillName, invocationKind, extra = {}) {
139
+ return {
140
+ _messageSplitIndex: identity.messageSplitIndex,
141
+ session_id: identity.sessionId,
142
+ seq: identity.seq,
143
+ skill_name: canonicalizeSkillName(skillName),
144
+ invocation_kind: invocationKind,
145
+ skill_path: null,
146
+ args_raw: null,
147
+ args_digest: null,
148
+ call_id: null,
149
+ status: 'ok',
150
+ started_at: null,
151
+ completed_at: null,
152
+ duration_ms: null,
153
+ ...extra,
154
+ };
155
+ }
156
+ /** Skill name from a `.../skills/<name>/SKILL.md` path; falls back to the SKILL.md sibling. */
157
+ function skillNameFromPath(path) {
158
+ const normalized = path.replace(/\\/g, '/');
159
+ const bySkillsSegment = normalized.match(/\/skills\/([^/]+)\/SKILL\.md$/i);
160
+ if (bySkillsSegment !== null)
161
+ return bySkillsSegment[1] ?? '';
162
+ const stem = normalized.slice(0, normalized.length - '/SKILL.md'.length);
163
+ return stem.split('/').pop() ?? stem;
164
+ }
165
+ function detectClaudeSkillCalls(raw, identity) {
166
+ const contentBlocks = raw.content ?? o(raw.message).content;
167
+ if (!Array.isArray(contentBlocks))
168
+ return [];
169
+ const rows = [];
170
+ for (const block of contentBlocks) {
171
+ if (typeof block !== 'object' || block === null)
172
+ continue;
173
+ const b = block;
174
+ if (b.type !== 'tool_use' || b.name !== 'Skill')
175
+ continue;
176
+ const input = (b.input ?? {});
177
+ const skill = s(input.skill);
178
+ if (skill === undefined)
179
+ continue;
180
+ // caller.type "direct" = user-invoked (storm report §10.3); absence = model-invoked.
181
+ rows.push(skillRecord(identity, skill, s(o(b.caller).type) === 'direct' ? 'user' : 'model', {
182
+ args_raw: maybeArgsRaw('claude', 'Skill', input) ?? null,
183
+ args_digest: argsDigest(input),
184
+ call_id: s(b.id) ?? null,
185
+ }));
186
+ }
187
+ return rows;
188
+ }
189
+ /** pi is inline-only: the `<skill name= location=>` wrapper in a user message is the sole trigger. */
190
+ const PI_SKILL_WRAPPER = /<skill\s+name="([^"]+)"(?:\s+location="([^"]*)")?\s*>/g;
191
+ function detectPiSkillCalls(raw, identity) {
192
+ const msg = raw.message;
193
+ if (piRole(msg?.role ?? raw.role ?? raw.recordType ?? raw.type) !== 'user')
194
+ return [];
195
+ const text = extractContentText(msg?.content ?? raw.content);
196
+ if (text === undefined)
197
+ return [];
198
+ const rows = [];
199
+ for (const match of text.matchAll(PI_SKILL_WRAPPER)) {
200
+ rows.push(skillRecord(identity, match[1] ?? '', 'user', { skill_path: match[2] || null }));
201
+ }
202
+ return rows;
203
+ }
204
+ function detectOmpSkillCalls(raw, identity) {
205
+ const msg = raw.message;
206
+ if (mapRole(msg?.role ?? raw.type ?? raw.role) !== 'assistant')
207
+ return [];
208
+ const contentBlocks = msg?.content ?? raw.content;
209
+ if (!Array.isArray(contentBlocks))
210
+ return [];
211
+ const rows = [];
212
+ for (const block of contentBlocks) {
213
+ const call = normalizeOmpToolCall(block);
214
+ if (call === null || call.name !== 'Skill')
215
+ continue;
216
+ const input = call.input ?? call.arguments;
217
+ const skill = s(input?.skill);
218
+ if (skill === undefined)
219
+ continue;
220
+ rows.push(skillRecord(identity, skill, 'model', {
221
+ args_raw: maybeArgsRaw('omp', 'Skill', input) ?? null,
222
+ args_digest: argsDigest(input),
223
+ call_id: s(call.id) ?? null,
224
+ }));
225
+ }
226
+ return rows;
227
+ }
228
+ /** Codex has no native load tool call: the child-element `<skill>` block is the trigger. */
229
+ const CODEX_SKILL_BLOCK = /<skill>\s*<name>([^<]+)<\/name>\s*<path>([^<]+)<\/path>\s*<\/skill>/g;
230
+ function detectCodexSkillCalls(raw, identity) {
231
+ const payload = (raw.payload ?? raw);
232
+ const recordType = String(raw.type ?? '');
233
+ const payloadType = String(payload.type ?? '');
234
+ let role;
235
+ if (recordType === 'response_item') {
236
+ role =
237
+ payloadType === 'message' ? mapRole(payload.role) : payloadType === 'agent_message' ? 'assistant' : 'meta';
238
+ }
239
+ else if (recordType === 'user' || recordType === 'assistant') {
240
+ role = mapRole(recordType);
241
+ }
242
+ else if (recordType === 'message') {
243
+ role = mapRole(raw.role ?? payload.role);
244
+ }
245
+ else {
246
+ return [];
247
+ }
248
+ if (role !== 'user')
249
+ return [];
250
+ const text = extractContentText(payload.content ?? raw.content) ?? s(payload.text, raw.text);
251
+ if (text === undefined)
252
+ return [];
253
+ const rows = [];
254
+ for (const match of text.matchAll(CODEX_SKILL_BLOCK)) {
255
+ rows.push(skillRecord(identity, match[1] ?? '', 'user', { skill_path: match[2] ?? null }));
256
+ }
257
+ return rows;
258
+ }
259
+ /** agy loads skills via `view_file` with toolAction "Viewing skill file" on a SKILL.md path. */
260
+ function detectAgySkillCalls(raw, identity) {
261
+ if (String(raw.type ?? '') !== 'PLANNER_RESPONSE' || !Array.isArray(raw.tool_calls))
262
+ return [];
263
+ const rows = [];
264
+ for (const tc of raw.tool_calls) {
265
+ const tool = s(tc.name, tc.tool_name);
266
+ if (tool !== 'view_file')
267
+ continue;
268
+ const args = (tc.args ?? tc.arguments ?? {});
269
+ if (args.toolAction !== 'Viewing skill file')
270
+ continue;
271
+ const path = s(args.AbsolutePath, args.absolute_path);
272
+ if (path === undefined || !path.replace(/\\/g, '/').endsWith('/SKILL.md'))
273
+ continue;
274
+ const summary = s(args.toolSummary);
275
+ const skillName = summary?.match(/SKILL\.md for (.+)$/)?.[1] ?? skillNameFromPath(path);
276
+ rows.push(skillRecord(identity, skillName, 'model', {
277
+ skill_path: path,
278
+ args_raw: maybeArgsRaw('agy', 'view_file', args) ?? null,
279
+ args_digest: argsDigest(args),
280
+ }));
281
+ }
282
+ return rows;
283
+ }
284
+ /** Gemini has no verified L1: the L0 harness prefix in a user message is the trigger. */
285
+ const GEMINI_L0_PREFIX = /^\s*\/((?:sp|rd3)-[a-z0-9-]+)/;
286
+ function detectGeminiSkillCalls(raw, identity) {
287
+ if (String(raw.type ?? '') !== 'user')
288
+ return [];
289
+ const content = geminiContent(raw);
290
+ if (content === null)
291
+ return [];
292
+ const match = content.match(GEMINI_L0_PREFIX);
293
+ if (match === null)
294
+ return [];
295
+ return [skillRecord(identity, match[1] ?? '', 'user')];
296
+ }
297
+ /** grok loads skills via `read_file` (grok_build namespace) targeting a SKILL.md path. */
298
+ function detectGrokSkillCalls(raw, identity) {
299
+ const n = normalizeGrokRecord(raw);
300
+ if (n.recordType !== 'tool_call' || n.toolName !== 'read_file')
301
+ return [];
302
+ const meta = (n.body._meta ?? raw._meta ?? {});
303
+ if (o(meta['x.ai/tool']).namespace !== 'grok_build')
304
+ return [];
305
+ const targetFile = s(o(n.toolArgs).target_file);
306
+ if (targetFile === undefined || !targetFile.replace(/\\/g, '/').endsWith('/SKILL.md'))
307
+ return [];
308
+ return [
309
+ skillRecord(identity, skillNameFromPath(targetFile), 'model', {
310
+ skill_path: targetFile,
311
+ args_raw: maybeArgsRaw('grok', 'read_file', n.toolArgs) ?? null,
312
+ args_digest: argsDigest(n.toolArgs),
313
+ started_at: n.ts ?? null,
314
+ }),
315
+ ];
316
+ }
317
+ // ---------------------------------------------------------------------------
57
318
  // Source-specific helpers
58
319
  // ---------------------------------------------------------------------------
59
320
  // Launch provenance (`spur-run` vs `ambient`) is a fact only the import host knows:
@@ -212,7 +473,7 @@ export function maybeArgsRaw(_source, toolName, args) {
212
473
  // Claude mapper
213
474
  // ---------------------------------------------------------------------------
214
475
  /** Map a Claude Code JSONL record into one or more ETL split entries. */
215
- export function claudeSplit(raw) {
476
+ export function claudeSplit(raw, context) {
216
477
  const entries = [];
217
478
  const sessionId = s(raw.sessionId, raw.conversation_uuid) ?? 'unknown';
218
479
  const seq = typeof raw.seq === 'number' ? raw.seq : typeof raw.messageIndex === 'number' ? raw.messageIndex : 0;
@@ -316,6 +577,10 @@ export function claudeSplit(raw) {
316
577
  }
317
578
  }
318
579
  }
580
+ // Skill-load rows (0736): claude's L1 is the native Skill tool_use; L0/L2 never trigger.
581
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
582
+ entries.push(skillCallEntry(record));
583
+ }
319
584
  return entries;
320
585
  }
321
586
  // ---------------------------------------------------------------------------
@@ -429,6 +694,10 @@ export function piSplit(raw, context) {
429
694
  });
430
695
  }
431
696
  }
697
+ // Skill-load rows (0736): pi is inline-only — the user-message wrapper is the sole trigger.
698
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
699
+ entries.push(skillCallEntry(record));
700
+ }
432
701
  return entries;
433
702
  }
434
703
  /**
@@ -552,6 +821,10 @@ export function ompSplit(raw, context) {
552
821
  });
553
822
  }
554
823
  }
824
+ // Skill-load rows (0736): omp's L1 is the native Skill toolCall; the L2 wrapper never triggers.
825
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
826
+ entries.push(skillCallEntry(record));
827
+ }
555
828
  return entries;
556
829
  }
557
830
  /**
@@ -824,6 +1097,10 @@ export function codexSplit(raw, context) {
824
1097
  },
825
1098
  });
826
1099
  }
1100
+ // Skill-load rows (0736): codex has no native load call — the child-element block is the trigger.
1101
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1102
+ entries.push(skillCallEntry(record));
1103
+ }
827
1104
  return entries;
828
1105
  }
829
1106
  // ---------------------------------------------------------------------------
@@ -955,6 +1232,10 @@ export function agySplit(raw, context) {
955
1232
  });
956
1233
  }
957
1234
  }
1235
+ // Skill-load rows (0736): agy's L1 is the view_file "Viewing skill file" tool call.
1236
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1237
+ entries.push(skillCallEntry(record));
1238
+ }
958
1239
  return entries;
959
1240
  }
960
1241
  // ---------------------------------------------------------------------------
@@ -1037,6 +1318,11 @@ export function geminiSplit(raw, context) {
1037
1318
  });
1038
1319
  }
1039
1320
  }
1321
+ // Skill-load rows (0736): gemini has no verified L1 — the L0 prefix in a user message triggers.
1322
+ // messageSplitIndex stays the literal 0, matching the gemini tool-call linkage above.
1323
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex: 0 })) {
1324
+ entries.push(skillCallEntry(record));
1325
+ }
1040
1326
  return entries;
1041
1327
  }
1042
1328
  const UUID_PATTERN = '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
@@ -1075,7 +1361,7 @@ function sessionIdFromContext(context, raw) {
1075
1361
  return s(raw.sessionId, raw.id) ?? 'unknown';
1076
1362
  }
1077
1363
  function geminiContent(raw) {
1078
- const content = extractContentText(raw.content);
1364
+ const content = extractContentText(raw.content) ?? s(raw.text);
1079
1365
  const thoughts = Array.isArray(raw.thoughts)
1080
1366
  ? raw.thoughts
1081
1367
  .map((value) => (value !== null && typeof value === 'object' ? s(o(value).description) : undefined))
@@ -1222,7 +1508,7 @@ const GROK_META_TYPES = new Set([
1222
1508
  'yolo_toggled',
1223
1509
  ]);
1224
1510
  /** Map a Grok JSONL record into one or more ETL split entries. */
1225
- export function grokSplit(raw) {
1511
+ export function grokSplit(raw, context) {
1226
1512
  const entries = [];
1227
1513
  const n = normalizeGrokRecord(raw);
1228
1514
  const { recordType, sessionId, ts, seq } = n;
@@ -1385,6 +1671,10 @@ export function grokSplit(raw) {
1385
1671
  error_text: n.errorText,
1386
1672
  },
1387
1673
  });
1674
+ // Skill-load rows (0736): grok's L1 is a grok_build read_file targeting SKILL.md.
1675
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1676
+ entries.push(skillCallEntry(record));
1677
+ }
1388
1678
  return entries;
1389
1679
  }
1390
1680
  if (recordType === 'user' ||
@@ -1601,19 +1891,19 @@ export function stableFieldShape(raw) {
1601
1891
  // Identity field maps for the six source mappers
1602
1892
  // ---------------------------------------------------------------------------
1603
1893
  /** Identity field map for the Claude mapper. */
1604
- export const CLAUDE_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1894
+ export const CLAUDE_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1605
1895
  /** Identity field map for the Pi mapper. */
1606
- export const PI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1896
+ export const PI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1607
1897
  /** Identity field map for the OMP mapper. */
1608
- export const OMP_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1898
+ export const OMP_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1609
1899
  /** Identity field map for the Codex mapper. */
1610
- export const CODEX_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1900
+ export const CODEX_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1611
1901
  /** Identity field map for the AGY mapper. */
1612
- export const AGY_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1902
+ export const AGY_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1613
1903
  /** Identity field map for the Grok mapper. */
1614
- export const GROK_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1904
+ export const GROK_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1615
1905
  /** Identity field map for the Gemini mapper. */
1616
- export const GEMINI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
1906
+ export const GEMINI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1617
1907
  // ---------------------------------------------------------------------------
1618
1908
  // Zod schemas for the six source mappers
1619
1909
  // ---------------------------------------------------------------------------
@@ -197,6 +197,41 @@ function prepareEntries(row, data, parts, sourceFile, redactionRules) {
197
197
  const output = stringValue(state.output);
198
198
  const status = stringValue(state.status) ?? 'unknown';
199
199
  const splitIndex = entries.length;
200
+ const toolName = stringValue(part.tool) ?? 'unknown';
201
+ // 0736: OpenCode's native `skill({name})` load tool is the skill-call trigger; it gets a
202
+ // history_skill_call row instead of a tool-call row (one row per load, same provenance).
203
+ if (toolName === 'skill') {
204
+ const input = objectValue(state.input);
205
+ const skillName = stringValue(input.name);
206
+ if (skillName !== undefined) {
207
+ const skillRecord = redactRecord({
208
+ message_hash: messageHash,
209
+ source: SOURCE,
210
+ source_file: sourceFile,
211
+ source_line: 1,
212
+ session_id: row.session_id,
213
+ seq: started ?? created,
214
+ skill_name: skillName,
215
+ invocation_kind: 'model',
216
+ skill_path: null,
217
+ args_raw: maybeArgsRaw(SOURCE, 'skill', state.input) ?? null,
218
+ args_digest: sha256(state.input ?? null),
219
+ status,
220
+ started_at: started === undefined ? null : isoTime(started),
221
+ completed_at: completedAt === undefined ? null : isoTime(completedAt),
222
+ duration_ms: started === undefined || completedAt === undefined
223
+ ? null
224
+ : Math.max(0, completedAt - started),
225
+ }, redactionRules);
226
+ entries.push({
227
+ targetTable: 'history_skill_call',
228
+ splitIndex,
229
+ record: skillRecord,
230
+ recordHash: recordHash(sourceFile, splitIndex, skillRecord),
231
+ });
232
+ continue;
233
+ }
234
+ }
200
235
  const toolRecord = redactRecord({
201
236
  message_hash: messageHash,
202
237
  source: SOURCE,
@@ -204,7 +239,7 @@ function prepareEntries(row, data, parts, sourceFile, redactionRules) {
204
239
  source_line: 1,
205
240
  session_id: row.session_id,
206
241
  seq: started ?? created,
207
- tool_name: stringValue(part.tool) ?? 'unknown',
242
+ tool_name: toolName,
208
243
  args_raw: maybeArgsRaw(SOURCE, stringValue(part.tool) ?? 'unknown', state.input),
209
244
  args_digest: sha256(state.input ?? null),
210
245
  status,
package/dist/types.d.ts CHANGED
@@ -128,6 +128,27 @@ export interface ImportResult {
128
128
  * load tool); `skill_path` is nullable because some producers inline the skill body without
129
129
  * a resolvable path.
130
130
  */
131
+ /**
132
+ * Split-time skill-load record routed to `history_skill_call` (0736). The importer/DAO manage
133
+ * `record_hash`, `message_hash` (resolved from `_messageSplitIndex`), the source identity
134
+ * columns, and `imported_at`; keeping them out of the split record also keeps them out of the
135
+ * record_hash input, so re-imports hash identically (0736 R5 idempotency).
136
+ */
137
+ export interface SkillCallSplitRecord {
138
+ readonly _messageSplitIndex?: number;
139
+ readonly session_id: string;
140
+ readonly seq: number;
141
+ readonly skill_name: string;
142
+ readonly invocation_kind: 'user' | 'model';
143
+ readonly skill_path?: string | null;
144
+ readonly args_raw?: string | null;
145
+ readonly args_digest?: string | null;
146
+ readonly call_id?: string | null;
147
+ readonly status?: string | null;
148
+ readonly started_at?: string | null;
149
+ readonly completed_at?: string | null;
150
+ readonly duration_ms?: number | null;
151
+ }
131
152
  export interface SkillCall {
132
153
  readonly record_hash: string;
133
154
  readonly message_hash: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-llm-jsonl-importer",
3
- "version": "0.4.52",
3
+ "version": "0.4.54",
4
4
  "description": "@gobing-ai/ts-llm-jsonl-importer — Generic JSONL importer for LLM agent history files.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,9 +47,9 @@
47
47
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-llm-jsonl-importer-v<version> && git push --tags' && exit 1"
48
48
  },
49
49
  "dependencies": {
50
- "@gobing-ai/ts-db": "^0.4.52",
51
- "@gobing-ai/ts-runtime": "^0.4.52",
52
- "@gobing-ai/ts-utils": "^0.4.52",
50
+ "@gobing-ai/ts-db": "^0.4.54",
51
+ "@gobing-ai/ts-runtime": "^0.4.54",
52
+ "@gobing-ai/ts-utils": "^0.4.54",
53
53
  "zod": "^4.1.0"
54
54
  },
55
55
  "devDependencies": {
package/src/index.ts CHANGED
@@ -2,7 +2,13 @@ export { HistoryImportError } from './errors';
2
2
  export { sha256, stableJson } from './hash';
3
3
  export { runJsonlImport } from './importer';
4
4
  export { applyHistoryImportSchema, normalizeSourceFilePaths } from './jsonl-importer-dao';
5
- export { sessionIdFromSourcePath } from './mappers';
5
+ export {
6
+ canonicalizeSkillName,
7
+ extractSkillCalls,
8
+ type SkillCallIdentity,
9
+ sessionIdFromSourcePath,
10
+ skillCallEntry,
11
+ } from './mappers';
6
12
  export { type OpenCodeImportOptions, runOpenCodeImport } from './opencode-importer';
7
13
  export { DEFAULT_REDACTION_RULES, redactRecord, redactValue } from './redaction';
8
14
  export { HISTORY_IMPORT_SCHEMA_SQL } from './schema-sql';
@@ -24,6 +30,7 @@ export type {
24
30
  ReconcileSummary,
25
31
  RedactionRule,
26
32
  SkillCall,
33
+ SkillCallSplitRecord,
27
34
  SourceDefinition,
28
35
  SplitConfig,
29
36
  SplitEntry,
@@ -667,7 +667,7 @@ export interface OpenCodeExistingEntry {
667
667
 
668
668
  /** Normalized OpenCode entry awaiting a history-table write. */
669
669
  export interface OpenCodeQueuedEntry {
670
- targetTable: 'history_message' | 'history_tool_call';
670
+ targetTable: 'history_message' | 'history_tool_call' | 'history_skill_call';
671
671
  splitIndex: number;
672
672
  record: JsonObject;
673
673
  recordHash: string;
@@ -752,7 +752,7 @@ export function openCodeBulkWriteOperations(
752
752
  ): DbBatchOp[] {
753
753
  const importedAt = timestamp(now);
754
754
  const operations: DbBatchOp[] = [];
755
- for (const table of ['history_message', 'history_tool_call'] as const) {
755
+ for (const table of ['history_message', 'history_tool_call', 'history_skill_call'] as const) {
756
756
  const tableEntries = entries.filter((entry) => entry.targetTable === table);
757
757
  for (let offset = 0; offset < tableEntries.length; offset += 100) {
758
758
  const chunk = tableEntries.slice(offset, offset + 100);
package/src/mappers.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { sha256 } from './hash';
3
- import type { JsonObject, SplitEntry, TransformContext } from './types';
3
+ import type { JsonObject, SkillCallSplitRecord, SplitEntry, TransformContext } from './types';
4
4
 
5
5
  // ---------------------------------------------------------------------------
6
6
  // Helper: identity field map for typed columns
@@ -50,6 +50,23 @@ const TOOL_CALL_MAPPER_KEYS: readonly string[] = [
50
50
  'error_text',
51
51
  ];
52
52
 
53
+ /** Columns the mapper may produce for history_skill_call (0736). */
54
+ const SKILL_CALL_MAPPER_KEYS: readonly string[] = [
55
+ '_messageSplitIndex',
56
+ 'session_id',
57
+ 'seq',
58
+ 'skill_name',
59
+ 'invocation_kind',
60
+ 'skill_path',
61
+ 'args_raw',
62
+ 'args_digest',
63
+ 'call_id',
64
+ 'status',
65
+ 'started_at',
66
+ 'completed_at',
67
+ 'duration_ms',
68
+ ];
69
+
53
70
  /** Build an identity fieldMap for the given column list. */
54
71
  function identityFieldMap(keys: readonly string[]): Record<string, string> {
55
72
  const map: Record<string, string> = {};
@@ -59,6 +76,261 @@ function identityFieldMap(keys: readonly string[]): Record<string, string> {
59
76
  return map;
60
77
  }
61
78
 
79
+ // ---------------------------------------------------------------------------
80
+ // Skill-load extraction (0736)
81
+ // ---------------------------------------------------------------------------
82
+
83
+ /** Harness skill packages whose dialect spelling swaps ':' for '-' (storm report §10.1). */
84
+ const HARNESS_SKILL_PACKAGES = new Set(['sp', 'rd3']);
85
+
86
+ /**
87
+ * Canonicalize a harness skill name: `sp-dev-run` / `$sp-dev-run` / `/skill:sp-dev-run` →
88
+ * `sp:dev-run`; `rd3-*` → `rd3:*`. Only the frozen harness package set is rewritten — an
89
+ * unqualified name like `code-review` is kept verbatim (exact structural match, 0736 R4).
90
+ */
91
+ export function canonicalizeSkillName(raw: string): string {
92
+ let name = raw.trim();
93
+ if (name.startsWith('/') || name.startsWith('$')) name = name.slice(1);
94
+ if (name.startsWith('skill:')) name = name.slice('skill:'.length);
95
+ const canonical = name.match(/^([a-z][a-z0-9]*):([a-z0-9-]+)$/);
96
+ if (canonical !== null && HARNESS_SKILL_PACKAGES.has(canonical[1] ?? '')) return name;
97
+ const dialect = name.match(/^([a-z][a-z0-9]*)-([a-z0-9-]+)$/);
98
+ if (dialect !== null && HARNESS_SKILL_PACKAGES.has(dialect[1] ?? '')) return `${dialect[1]}:${dialect[2]}`;
99
+ return name;
100
+ }
101
+
102
+ /** Identity fields a split already computed for its parent message (skill rows join it). */
103
+ export interface SkillCallIdentity {
104
+ readonly sessionId: string;
105
+ readonly seq: number;
106
+ /** Split index of the parent message entry in the same split batch (_messageSplitIndex). */
107
+ readonly messageSplitIndex: number;
108
+ }
109
+
110
+ /**
111
+ * Detect skill-load events in one raw source record (0736 R1/R2). Dispatches on the source in
112
+ * the transform context; unknown sources return nothing. Detection signatures per source are
113
+ * the verified ones from the storm report §10.3:
114
+ *
115
+ * - L1 native load tool is authoritative (claude/omp `Skill`, agy `view_file` skill reads,
116
+ * grok `read_file` on SKILL.md, opencode native `skill`).
117
+ * - Sources with no L1 trigger on their structural signal: pi's `<skill name= location=>`
118
+ * wrapper, codex's `<skill><name>/<path>` block, gemini's L0 harness prefix.
119
+ * - L0/L2 never trigger for agents that have an L1 (false-positive suppression, 0736 R4).
120
+ */
121
+ export function extractSkillCalls(
122
+ raw: JsonObject,
123
+ context: TransformContext | undefined,
124
+ identity: SkillCallIdentity,
125
+ ): readonly SkillCallSplitRecord[] {
126
+ switch (context?.source) {
127
+ case 'claude':
128
+ return detectClaudeSkillCalls(raw, identity);
129
+ case 'pi':
130
+ return detectPiSkillCalls(raw, identity);
131
+ case 'omp':
132
+ return detectOmpSkillCalls(raw, identity);
133
+ case 'codex':
134
+ return detectCodexSkillCalls(raw, identity);
135
+ case 'agy':
136
+ return detectAgySkillCalls(raw, identity);
137
+ case 'gemini':
138
+ return detectGeminiSkillCalls(raw, identity);
139
+ case 'grok':
140
+ return detectGrokSkillCalls(raw, identity);
141
+ default:
142
+ return [];
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Route one skill-load row to `history_skill_call` exactly like tool calls route to
148
+ * `history_tool_call`: a SplitEntry whose record the importer normalizes, hashes, dedups,
149
+ * and inserts through the typed-column path.
150
+ */
151
+ export function skillCallEntry(record: SkillCallSplitRecord): SplitEntry {
152
+ // The split record's keys are exactly the history_skill_call typed columns; the index
153
+ // signature is supplied at this boundary (runtime shape is JSON-compatible by construction).
154
+ return { targetTable: 'history_skill_call', record: record as unknown as JsonObject };
155
+ }
156
+
157
+ /** Build a fully-populated skill row: every optional column set explicitly for hash stability. */
158
+ function skillRecord(
159
+ identity: SkillCallIdentity,
160
+ skillName: string,
161
+ invocationKind: 'user' | 'model',
162
+ extra: Partial<SkillCallSplitRecord> = {},
163
+ ): SkillCallSplitRecord {
164
+ return {
165
+ _messageSplitIndex: identity.messageSplitIndex,
166
+ session_id: identity.sessionId,
167
+ seq: identity.seq,
168
+ skill_name: canonicalizeSkillName(skillName),
169
+ invocation_kind: invocationKind,
170
+ skill_path: null,
171
+ args_raw: null,
172
+ args_digest: null,
173
+ call_id: null,
174
+ status: 'ok',
175
+ started_at: null,
176
+ completed_at: null,
177
+ duration_ms: null,
178
+ ...extra,
179
+ };
180
+ }
181
+
182
+ /** Skill name from a `.../skills/<name>/SKILL.md` path; falls back to the SKILL.md sibling. */
183
+ function skillNameFromPath(path: string): string {
184
+ const normalized = path.replace(/\\/g, '/');
185
+ const bySkillsSegment = normalized.match(/\/skills\/([^/]+)\/SKILL\.md$/i);
186
+ if (bySkillsSegment !== null) return bySkillsSegment[1] ?? '';
187
+ const stem = normalized.slice(0, normalized.length - '/SKILL.md'.length);
188
+ return stem.split('/').pop() ?? stem;
189
+ }
190
+
191
+ function detectClaudeSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
192
+ const contentBlocks = raw.content ?? o(raw.message).content;
193
+ if (!Array.isArray(contentBlocks)) return [];
194
+ const rows: SkillCallSplitRecord[] = [];
195
+ for (const block of contentBlocks) {
196
+ if (typeof block !== 'object' || block === null) continue;
197
+ const b = block as Record<string, unknown>;
198
+ if (b.type !== 'tool_use' || b.name !== 'Skill') continue;
199
+ const input = (b.input ?? {}) as Record<string, unknown>;
200
+ const skill = s(input.skill);
201
+ if (skill === undefined) continue;
202
+ // caller.type "direct" = user-invoked (storm report §10.3); absence = model-invoked.
203
+ rows.push(
204
+ skillRecord(identity, skill, s(o(b.caller).type) === 'direct' ? 'user' : 'model', {
205
+ args_raw: maybeArgsRaw('claude', 'Skill', input) ?? null,
206
+ args_digest: argsDigest(input),
207
+ call_id: s(b.id) ?? null,
208
+ }),
209
+ );
210
+ }
211
+ return rows;
212
+ }
213
+
214
+ /** pi is inline-only: the `<skill name= location=>` wrapper in a user message is the sole trigger. */
215
+ const PI_SKILL_WRAPPER = /<skill\s+name="([^"]+)"(?:\s+location="([^"]*)")?\s*>/g;
216
+
217
+ function detectPiSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
218
+ const msg = raw.message as Record<string, unknown> | undefined;
219
+ if (piRole(msg?.role ?? raw.role ?? raw.recordType ?? raw.type) !== 'user') return [];
220
+ const text = extractContentText(msg?.content ?? raw.content);
221
+ if (text === undefined) return [];
222
+ const rows: SkillCallSplitRecord[] = [];
223
+ for (const match of text.matchAll(PI_SKILL_WRAPPER)) {
224
+ rows.push(skillRecord(identity, match[1] ?? '', 'user', { skill_path: match[2] || null }));
225
+ }
226
+ return rows;
227
+ }
228
+
229
+ function detectOmpSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
230
+ const msg = raw.message as Record<string, unknown> | undefined;
231
+ if (mapRole(msg?.role ?? raw.type ?? raw.role) !== 'assistant') return [];
232
+ const contentBlocks = msg?.content ?? raw.content;
233
+ if (!Array.isArray(contentBlocks)) return [];
234
+ const rows: SkillCallSplitRecord[] = [];
235
+ for (const block of contentBlocks as Record<string, unknown>[]) {
236
+ const call = normalizeOmpToolCall(block);
237
+ if (call === null || call.name !== 'Skill') continue;
238
+ const input = call.input ?? call.arguments;
239
+ const skill = s((input as Record<string, unknown> | undefined)?.skill);
240
+ if (skill === undefined) continue;
241
+ rows.push(
242
+ skillRecord(identity, skill, 'model', {
243
+ args_raw: maybeArgsRaw('omp', 'Skill', input) ?? null,
244
+ args_digest: argsDigest(input),
245
+ call_id: s(call.id) ?? null,
246
+ }),
247
+ );
248
+ }
249
+ return rows;
250
+ }
251
+
252
+ /** Codex has no native load tool call: the child-element `<skill>` block is the trigger. */
253
+ const CODEX_SKILL_BLOCK = /<skill>\s*<name>([^<]+)<\/name>\s*<path>([^<]+)<\/path>\s*<\/skill>/g;
254
+
255
+ function detectCodexSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
256
+ const payload = (raw.payload ?? raw) as Record<string, unknown>;
257
+ const recordType = String(raw.type ?? '');
258
+ const payloadType = String(payload.type ?? '');
259
+ let role: string;
260
+ if (recordType === 'response_item') {
261
+ role =
262
+ payloadType === 'message' ? mapRole(payload.role) : payloadType === 'agent_message' ? 'assistant' : 'meta';
263
+ } else if (recordType === 'user' || recordType === 'assistant') {
264
+ role = mapRole(recordType);
265
+ } else if (recordType === 'message') {
266
+ role = mapRole(raw.role ?? payload.role);
267
+ } else {
268
+ return [];
269
+ }
270
+ if (role !== 'user') return [];
271
+ const text = extractContentText(payload.content ?? raw.content) ?? s(payload.text, raw.text);
272
+ if (text === undefined) return [];
273
+ const rows: SkillCallSplitRecord[] = [];
274
+ for (const match of text.matchAll(CODEX_SKILL_BLOCK)) {
275
+ rows.push(skillRecord(identity, match[1] ?? '', 'user', { skill_path: match[2] ?? null }));
276
+ }
277
+ return rows;
278
+ }
279
+
280
+ /** agy loads skills via `view_file` with toolAction "Viewing skill file" on a SKILL.md path. */
281
+ function detectAgySkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
282
+ if (String(raw.type ?? '') !== 'PLANNER_RESPONSE' || !Array.isArray(raw.tool_calls)) return [];
283
+ const rows: SkillCallSplitRecord[] = [];
284
+ for (const tc of raw.tool_calls as Record<string, unknown>[]) {
285
+ const tool = s(tc.name, tc.tool_name);
286
+ if (tool !== 'view_file') continue;
287
+ const args = (tc.args ?? tc.arguments ?? {}) as Record<string, unknown>;
288
+ if (args.toolAction !== 'Viewing skill file') continue;
289
+ const path = s(args.AbsolutePath, args.absolute_path);
290
+ if (path === undefined || !path.replace(/\\/g, '/').endsWith('/SKILL.md')) continue;
291
+ const summary = s(args.toolSummary);
292
+ const skillName = summary?.match(/SKILL\.md for (.+)$/)?.[1] ?? skillNameFromPath(path);
293
+ rows.push(
294
+ skillRecord(identity, skillName, 'model', {
295
+ skill_path: path,
296
+ args_raw: maybeArgsRaw('agy', 'view_file', args) ?? null,
297
+ args_digest: argsDigest(args),
298
+ }),
299
+ );
300
+ }
301
+ return rows;
302
+ }
303
+
304
+ /** Gemini has no verified L1: the L0 harness prefix in a user message is the trigger. */
305
+ const GEMINI_L0_PREFIX = /^\s*\/((?:sp|rd3)-[a-z0-9-]+)/;
306
+
307
+ function detectGeminiSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
308
+ if (String(raw.type ?? '') !== 'user') return [];
309
+ const content = geminiContent(raw);
310
+ if (content === null) return [];
311
+ const match = content.match(GEMINI_L0_PREFIX);
312
+ if (match === null) return [];
313
+ return [skillRecord(identity, match[1] ?? '', 'user')];
314
+ }
315
+
316
+ /** grok loads skills via `read_file` (grok_build namespace) targeting a SKILL.md path. */
317
+ function detectGrokSkillCalls(raw: JsonObject, identity: SkillCallIdentity): readonly SkillCallSplitRecord[] {
318
+ const n = normalizeGrokRecord(raw);
319
+ if (n.recordType !== 'tool_call' || n.toolName !== 'read_file') return [];
320
+ const meta = (n.body._meta ?? raw._meta ?? {}) as Record<string, unknown>;
321
+ if (o(meta['x.ai/tool']).namespace !== 'grok_build') return [];
322
+ const targetFile = s(o(n.toolArgs).target_file);
323
+ if (targetFile === undefined || !targetFile.replace(/\\/g, '/').endsWith('/SKILL.md')) return [];
324
+ return [
325
+ skillRecord(identity, skillNameFromPath(targetFile), 'model', {
326
+ skill_path: targetFile,
327
+ args_raw: maybeArgsRaw('grok', 'read_file', n.toolArgs) ?? null,
328
+ args_digest: argsDigest(n.toolArgs),
329
+ started_at: n.ts ?? null,
330
+ }),
331
+ ];
332
+ }
333
+
62
334
  // ---------------------------------------------------------------------------
63
335
  // Source-specific helpers
64
336
  // ---------------------------------------------------------------------------
@@ -231,7 +503,7 @@ export function maybeArgsRaw(_source: string, toolName: string, args: unknown):
231
503
  // ---------------------------------------------------------------------------
232
504
 
233
505
  /** Map a Claude Code JSONL record into one or more ETL split entries. */
234
- export function claudeSplit(raw: Record<string, unknown>): readonly SplitEntry[] {
506
+ export function claudeSplit(raw: Record<string, unknown>, context?: TransformContext): readonly SplitEntry[] {
235
507
  const entries: SplitEntry[] = [];
236
508
  const sessionId = s(raw.sessionId, raw.conversation_uuid) ?? 'unknown';
237
509
  const seq = typeof raw.seq === 'number' ? raw.seq : typeof raw.messageIndex === 'number' ? raw.messageIndex : 0;
@@ -344,6 +616,11 @@ export function claudeSplit(raw: Record<string, unknown>): readonly SplitEntry[]
344
616
  }
345
617
  }
346
618
 
619
+ // Skill-load rows (0736): claude's L1 is the native Skill tool_use; L0/L2 never trigger.
620
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
621
+ entries.push(skillCallEntry(record));
622
+ }
623
+
347
624
  return entries;
348
625
  }
349
626
 
@@ -466,6 +743,11 @@ export function piSplit(raw: Record<string, unknown>, context?: TransformContext
466
743
  }
467
744
  }
468
745
 
746
+ // Skill-load rows (0736): pi is inline-only — the user-message wrapper is the sole trigger.
747
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
748
+ entries.push(skillCallEntry(record));
749
+ }
750
+
469
751
  return entries;
470
752
  }
471
753
 
@@ -597,6 +879,11 @@ export function ompSplit(raw: Record<string, unknown>, context?: TransformContex
597
879
  }
598
880
  }
599
881
 
882
+ // Skill-load rows (0736): omp's L1 is the native Skill toolCall; the L2 wrapper never triggers.
883
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
884
+ entries.push(skillCallEntry(record));
885
+ }
886
+
600
887
  return entries;
601
888
  }
602
889
 
@@ -908,6 +1195,11 @@ export function codexSplit(raw: Record<string, unknown>, context?: TransformCont
908
1195
  });
909
1196
  }
910
1197
 
1198
+ // Skill-load rows (0736): codex has no native load call — the child-element block is the trigger.
1199
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1200
+ entries.push(skillCallEntry(record));
1201
+ }
1202
+
911
1203
  return entries;
912
1204
  }
913
1205
 
@@ -1046,6 +1338,11 @@ export function agySplit(raw: Record<string, unknown>, context?: TransformContex
1046
1338
  }
1047
1339
  }
1048
1340
 
1341
+ // Skill-load rows (0736): agy's L1 is the view_file "Viewing skill file" tool call.
1342
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1343
+ entries.push(skillCallEntry(record));
1344
+ }
1345
+
1049
1346
  return entries;
1050
1347
  }
1051
1348
 
@@ -1134,6 +1431,12 @@ export function geminiSplit(raw: Record<string, unknown>, context?: TransformCon
1134
1431
  }
1135
1432
  }
1136
1433
 
1434
+ // Skill-load rows (0736): gemini has no verified L1 — the L0 prefix in a user message triggers.
1435
+ // messageSplitIndex stays the literal 0, matching the gemini tool-call linkage above.
1436
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex: 0 })) {
1437
+ entries.push(skillCallEntry(record));
1438
+ }
1439
+
1137
1440
  return entries;
1138
1441
  }
1139
1442
 
@@ -1175,7 +1478,7 @@ function sessionIdFromContext(context: TransformContext | undefined, raw: Record
1175
1478
  }
1176
1479
 
1177
1480
  function geminiContent(raw: Record<string, unknown>): string | null {
1178
- const content = extractContentText(raw.content);
1481
+ const content = extractContentText(raw.content) ?? s(raw.text);
1179
1482
  const thoughts = Array.isArray(raw.thoughts)
1180
1483
  ? raw.thoughts
1181
1484
  .map((value) => (value !== null && typeof value === 'object' ? s(o(value).description) : undefined))
@@ -1358,7 +1661,7 @@ const GROK_META_TYPES = new Set([
1358
1661
  ]);
1359
1662
 
1360
1663
  /** Map a Grok JSONL record into one or more ETL split entries. */
1361
- export function grokSplit(raw: Record<string, unknown>): readonly SplitEntry[] {
1664
+ export function grokSplit(raw: Record<string, unknown>, context?: TransformContext): readonly SplitEntry[] {
1362
1665
  const entries: SplitEntry[] = [];
1363
1666
  const n = normalizeGrokRecord(raw);
1364
1667
  const { recordType, sessionId, ts, seq } = n;
@@ -1527,6 +1830,10 @@ export function grokSplit(raw: Record<string, unknown>): readonly SplitEntry[] {
1527
1830
  error_text: n.errorText,
1528
1831
  },
1529
1832
  });
1833
+ // Skill-load rows (0736): grok's L1 is a grok_build read_file targeting SKILL.md.
1834
+ for (const record of extractSkillCalls(raw, context, { sessionId, seq, messageSplitIndex })) {
1835
+ entries.push(skillCallEntry(record));
1836
+ }
1530
1837
  return entries;
1531
1838
  }
1532
1839
 
@@ -1753,19 +2060,31 @@ export function stableFieldShape(raw: JsonObject): string {
1753
2060
  // ---------------------------------------------------------------------------
1754
2061
 
1755
2062
  /** Identity field map for the Claude mapper. */
1756
- export const CLAUDE_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2063
+ export const CLAUDE_FIELD_MAP = identityFieldMap(
2064
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2065
+ );
1757
2066
  /** Identity field map for the Pi mapper. */
1758
- export const PI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2067
+ export const PI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS));
1759
2068
  /** Identity field map for the OMP mapper. */
1760
- export const OMP_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2069
+ export const OMP_FIELD_MAP = identityFieldMap(
2070
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2071
+ );
1761
2072
  /** Identity field map for the Codex mapper. */
1762
- export const CODEX_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2073
+ export const CODEX_FIELD_MAP = identityFieldMap(
2074
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2075
+ );
1763
2076
  /** Identity field map for the AGY mapper. */
1764
- export const AGY_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2077
+ export const AGY_FIELD_MAP = identityFieldMap(
2078
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2079
+ );
1765
2080
  /** Identity field map for the Grok mapper. */
1766
- export const GROK_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2081
+ export const GROK_FIELD_MAP = identityFieldMap(
2082
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2083
+ );
1767
2084
  /** Identity field map for the Gemini mapper. */
1768
- export const GEMINI_FIELD_MAP = identityFieldMap(MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS));
2085
+ export const GEMINI_FIELD_MAP = identityFieldMap(
2086
+ MESSAGE_MAPPER_KEYS.concat(TOOL_CALL_MAPPER_KEYS, SKILL_CALL_MAPPER_KEYS),
2087
+ );
1769
2088
 
1770
2089
  // ---------------------------------------------------------------------------
1771
2090
  // Zod schemas for the six source mappers
@@ -27,7 +27,7 @@ const SOURCE = 'opencode';
27
27
  const PAGE_SIZE = 250;
28
28
 
29
29
  interface PreparedEntry {
30
- targetTable: 'history_message' | 'history_tool_call';
30
+ targetTable: 'history_message' | 'history_tool_call' | 'history_skill_call';
31
31
  splitIndex: number;
32
32
  record: JsonObject;
33
33
  recordHash: string;
@@ -256,6 +256,45 @@ function prepareEntries(
256
256
  const output = stringValue(state.output);
257
257
  const status = stringValue(state.status) ?? 'unknown';
258
258
  const splitIndex = entries.length;
259
+ const toolName = stringValue(part.tool) ?? 'unknown';
260
+ // 0736: OpenCode's native `skill({name})` load tool is the skill-call trigger; it gets a
261
+ // history_skill_call row instead of a tool-call row (one row per load, same provenance).
262
+ if (toolName === 'skill') {
263
+ const input = objectValue(state.input);
264
+ const skillName = stringValue(input.name);
265
+ if (skillName !== undefined) {
266
+ const skillRecord = redactRecord(
267
+ {
268
+ message_hash: messageHash,
269
+ source: SOURCE,
270
+ source_file: sourceFile,
271
+ source_line: 1,
272
+ session_id: row.session_id,
273
+ seq: started ?? created,
274
+ skill_name: skillName,
275
+ invocation_kind: 'model',
276
+ skill_path: null,
277
+ args_raw: maybeArgsRaw(SOURCE, 'skill', state.input) ?? null,
278
+ args_digest: sha256(state.input ?? null),
279
+ status,
280
+ started_at: started === undefined ? null : isoTime(started),
281
+ completed_at: completedAt === undefined ? null : isoTime(completedAt),
282
+ duration_ms:
283
+ started === undefined || completedAt === undefined
284
+ ? null
285
+ : Math.max(0, completedAt - started),
286
+ },
287
+ redactionRules,
288
+ );
289
+ entries.push({
290
+ targetTable: 'history_skill_call',
291
+ splitIndex,
292
+ record: skillRecord,
293
+ recordHash: recordHash(sourceFile, splitIndex, skillRecord),
294
+ });
295
+ continue;
296
+ }
297
+ }
259
298
  const toolRecord = redactRecord(
260
299
  {
261
300
  message_hash: messageHash,
@@ -264,7 +303,7 @@ function prepareEntries(
264
303
  source_line: 1,
265
304
  session_id: row.session_id,
266
305
  seq: started ?? created,
267
- tool_name: stringValue(part.tool) ?? 'unknown',
306
+ tool_name: toolName,
268
307
  args_raw: maybeArgsRaw(SOURCE, stringValue(part.tool) ?? 'unknown', state.input),
269
308
  args_digest: sha256(state.input ?? null),
270
309
  status,
package/src/types.ts CHANGED
@@ -155,6 +155,28 @@ export interface ImportResult {
155
155
  * load tool); `skill_path` is nullable because some producers inline the skill body without
156
156
  * a resolvable path.
157
157
  */
158
+ /**
159
+ * Split-time skill-load record routed to `history_skill_call` (0736). The importer/DAO manage
160
+ * `record_hash`, `message_hash` (resolved from `_messageSplitIndex`), the source identity
161
+ * columns, and `imported_at`; keeping them out of the split record also keeps them out of the
162
+ * record_hash input, so re-imports hash identically (0736 R5 idempotency).
163
+ */
164
+ export interface SkillCallSplitRecord {
165
+ readonly _messageSplitIndex?: number;
166
+ readonly session_id: string;
167
+ readonly seq: number;
168
+ readonly skill_name: string;
169
+ readonly invocation_kind: 'user' | 'model';
170
+ readonly skill_path?: string | null;
171
+ readonly args_raw?: string | null;
172
+ readonly args_digest?: string | null;
173
+ readonly call_id?: string | null;
174
+ readonly status?: string | null;
175
+ readonly started_at?: string | null;
176
+ readonly completed_at?: string | null;
177
+ readonly duration_ms?: number | null;
178
+ }
179
+
158
180
  export interface SkillCall {
159
181
  readonly record_hash: string;
160
182
  readonly message_hash: string;