@jinn-network/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/canonical-json.d.ts +1 -0
  2. package/dist/canonical-json.js +67 -0
  3. package/dist/captured-task.d.ts +140 -0
  4. package/dist/captured-task.js +122 -0
  5. package/dist/contribution-store.d.ts +97 -0
  6. package/dist/contribution-store.js +770 -0
  7. package/dist/corpus-read/acquire.d.ts +39 -0
  8. package/dist/corpus-read/acquire.js +212 -0
  9. package/dist/corpus-read/cache.d.ts +14 -0
  10. package/dist/corpus-read/cache.js +17 -0
  11. package/dist/corpus-read/capture-meta.d.ts +16 -0
  12. package/dist/corpus-read/capture-meta.js +18 -0
  13. package/dist/corpus-read/create-corpus.d.ts +2 -0
  14. package/dist/corpus-read/create-corpus.js +97 -0
  15. package/dist/corpus-read/fetch-artifact.d.ts +21 -0
  16. package/dist/corpus-read/fetch-artifact.js +32 -0
  17. package/dist/corpus-read/fetch.d.ts +12 -0
  18. package/dist/corpus-read/fetch.js +24 -0
  19. package/dist/corpus-read/http-discovery.d.ts +9 -0
  20. package/dist/corpus-read/http-discovery.js +128 -0
  21. package/dist/corpus-read/index.d.ts +10 -0
  22. package/dist/corpus-read/index.js +10 -0
  23. package/dist/corpus-read/ipfs.d.ts +5 -0
  24. package/dist/corpus-read/ipfs.js +72 -0
  25. package/dist/corpus-read/route-resolver.d.ts +16 -0
  26. package/dist/corpus-read/route-resolver.js +19 -0
  27. package/dist/corpus-read/types.d.ts +177 -0
  28. package/dist/corpus-read/types.js +42 -0
  29. package/dist/envelope.d.ts +134 -0
  30. package/dist/envelope.js +162 -0
  31. package/dist/evidence-adapter.d.ts +26 -0
  32. package/dist/evidence-adapter.js +321 -0
  33. package/dist/evidence-filesystem.d.ts +40 -0
  34. package/dist/evidence-filesystem.js +267 -0
  35. package/dist/evidence-index.d.ts +117 -0
  36. package/dist/evidence-index.js +1083 -0
  37. package/dist/evidence-store-lock.d.ts +2 -0
  38. package/dist/evidence-store-lock.js +208 -0
  39. package/dist/execution-envelope.d.ts +3142 -0
  40. package/dist/execution-envelope.js +203 -0
  41. package/dist/index.d.ts +12 -0
  42. package/dist/index.js +12 -0
  43. package/dist/manifest.d.ts +75 -0
  44. package/dist/manifest.js +184 -0
  45. package/dist/paired.d.ts +68 -0
  46. package/dist/paired.js +92 -0
  47. package/dist/scrub/build.d.ts +52 -0
  48. package/dist/scrub/build.js +84 -0
  49. package/dist/scrub/emit-scrub.d.ts +41 -0
  50. package/dist/scrub/emit-scrub.js +87 -0
  51. package/dist/scrub/index.d.ts +12 -0
  52. package/dist/scrub/index.js +12 -0
  53. package/dist/scrub/key-policy.d.ts +20 -0
  54. package/dist/scrub/key-policy.js +42 -0
  55. package/dist/scrub/layer2.d.ts +23 -0
  56. package/dist/scrub/layer2.js +32 -0
  57. package/dist/scrub/ml-pii-stage.d.ts +24 -0
  58. package/dist/scrub/ml-pii-stage.js +42 -0
  59. package/dist/scrub/openredaction-stage.d.ts +196 -0
  60. package/dist/scrub/openredaction-stage.js +270 -0
  61. package/dist/scrub/pii-build.d.ts +38 -0
  62. package/dist/scrub/pii-build.js +67 -0
  63. package/dist/scrub/pipeline.d.ts +38 -0
  64. package/dist/scrub/pipeline.js +83 -0
  65. package/dist/scrub/plain-patterns-stage.d.ts +31 -0
  66. package/dist/scrub/plain-patterns-stage.js +84 -0
  67. package/dist/scrub/secretlint-stage.d.ts +36 -0
  68. package/dist/scrub/secretlint-stage.js +249 -0
  69. package/dist/scrub/transformers-detector.d.ts +35 -0
  70. package/dist/scrub/transformers-detector.js +67 -0
  71. package/dist/scrub/types.d.ts +25 -0
  72. package/dist/scrub/types.js +1 -0
  73. package/dist/session-provenance.d.ts +75 -0
  74. package/dist/session-provenance.js +20 -0
  75. package/dist/skill-artifact.d.ts +442 -0
  76. package/dist/skill-artifact.js +136 -0
  77. package/dist/trajectory/hash-chain.d.ts +18 -0
  78. package/dist/trajectory/hash-chain.js +47 -0
  79. package/dist/trajectory/index.d.ts +4 -0
  80. package/dist/trajectory/index.js +4 -0
  81. package/dist/trajectory/schema.d.ts +606 -0
  82. package/dist/trajectory/schema.js +110 -0
  83. package/dist/trajectory/transcript-parsers/aider-history.d.ts +42 -0
  84. package/dist/trajectory/transcript-parsers/aider-history.js +181 -0
  85. package/dist/trajectory/transcript-parsers/claude-code-jsonl.d.ts +37 -0
  86. package/dist/trajectory/transcript-parsers/claude-code-jsonl.js +142 -0
  87. package/dist/trajectory/transcript-parsers/codex-session.d.ts +42 -0
  88. package/dist/trajectory/transcript-parsers/codex-session.js +235 -0
  89. package/dist/trajectory/transcript-parsers/continue-devdata.d.ts +41 -0
  90. package/dist/trajectory/transcript-parsers/continue-devdata.js +190 -0
  91. package/dist/trajectory/transcript-parsers/cursor-sqlite.d.ts +38 -0
  92. package/dist/trajectory/transcript-parsers/cursor-sqlite.js +118 -0
  93. package/dist/trajectory/transcript-parsers/gemini-session.d.ts +35 -0
  94. package/dist/trajectory/transcript-parsers/gemini-session.js +137 -0
  95. package/dist/trajectory/transcript-parsers/index.d.ts +7 -0
  96. package/dist/trajectory/transcript-parsers/index.js +7 -0
  97. package/dist/trajectory/transcript-parsers/types.d.ts +101 -0
  98. package/dist/trajectory/transcript-parsers/types.js +32 -0
  99. package/dist/trajectory/transcript-to-spans/attrs.d.ts +4 -0
  100. package/dist/trajectory/transcript-to-spans/attrs.js +30 -0
  101. package/dist/trajectory/transcript-to-spans/claude-code-stream-json.d.ts +8 -0
  102. package/dist/trajectory/transcript-to-spans/claude-code-stream-json.js +162 -0
  103. package/dist/trajectory/transcript-to-spans/codex-exec-json.d.ts +8 -0
  104. package/dist/trajectory/transcript-to-spans/codex-exec-json.js +253 -0
  105. package/dist/trajectory/transcript-to-spans/index.d.ts +4 -0
  106. package/dist/trajectory/transcript-to-spans/index.js +4 -0
  107. package/dist/trajectory/transcript-to-spans/types.d.ts +18 -0
  108. package/dist/trajectory/transcript-to-spans/types.js +1 -0
  109. package/dist/window.d.ts +12 -0
  110. package/dist/window.js +5 -0
  111. package/package.json +67 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Gemini CLI session transcript parser.
3
+ *
4
+ * Reads `~/.gemini/sessions/<id>.jsonl` (or comparable path) line-by-line,
5
+ * mapping per-record content to canonical TranscriptEvent shapes (Task 3.1).
6
+ *
7
+ * Gemini's content shape follows the GenerativeAI Content schema: each record
8
+ * has a `role` ('user' or 'model') and a `parts` array. Each part may be:
9
+ * - { text: string } → user-message / assistant-message
10
+ * - { functionCall: { name, args } } → tool-call
11
+ * - { functionResponse: { name, response } } → tool-result
12
+ *
13
+ * Multiple parts in a single record can produce multiple events, mirroring
14
+ * the Claude Code parser's tool_use-block handling. Text parts within the
15
+ * same model record are concatenated into a single assistant-message; mixing
16
+ * text and functionCall parts emits both, in order.
17
+ *
18
+ * `parseChunk` preserves any incomplete trailing line across calls.
19
+ *
20
+ * Spec: spec/2026-05-07-telemetry-collector-and-task-generator.md §4.2 path B.
21
+ */
22
+ import { readFile } from 'node:fs/promises';
23
+ export class GeminiSessionParser {
24
+ tool = 'gemini-cli';
25
+ buffer = '';
26
+ parseChunk(input) {
27
+ const text = typeof input.chunk === 'string' ? input.chunk : input.chunk.toString('utf-8');
28
+ this.buffer += text;
29
+ const lines = this.buffer.split('\n');
30
+ this.buffer = lines.pop() ?? '';
31
+ const events = [];
32
+ for (const line of lines) {
33
+ if (!line.trim())
34
+ continue;
35
+ let record;
36
+ try {
37
+ record = JSON.parse(line);
38
+ }
39
+ catch {
40
+ continue;
41
+ }
42
+ events.push(...this.recordToEvents(record));
43
+ }
44
+ return events;
45
+ }
46
+ async parseFull(input) {
47
+ const text = await readFile(input.path, 'utf-8');
48
+ const normalised = text.endsWith('\n') ? text : text + '\n';
49
+ const previousBuffer = this.buffer;
50
+ this.buffer = '';
51
+ try {
52
+ return this.parseChunk({ sessionId: input.sessionId, chunk: normalised });
53
+ }
54
+ finally {
55
+ this.buffer = previousBuffer;
56
+ }
57
+ }
58
+ recordToEvents(record) {
59
+ if (!record || typeof record !== 'object')
60
+ return [];
61
+ const ts = record.timestamp ?? record.ts;
62
+ if (typeof ts !== 'string')
63
+ return [];
64
+ if (!Array.isArray(record.parts))
65
+ return [];
66
+ const role = record.role;
67
+ const out = [];
68
+ for (const part of record.parts) {
69
+ if (!part || typeof part !== 'object')
70
+ continue;
71
+ if (isTextPart(part)) {
72
+ if (role === 'user') {
73
+ out.push({ kind: 'user-message', timestamp: ts, content: part.text });
74
+ }
75
+ else if (role === 'model' || role === 'assistant') {
76
+ out.push({ kind: 'assistant-message', timestamp: ts, content: part.text });
77
+ }
78
+ continue;
79
+ }
80
+ if (isFunctionCallPart(part)) {
81
+ out.push({
82
+ kind: 'tool-call',
83
+ timestamp: ts,
84
+ name: part.functionCall.name,
85
+ args: part.functionCall.args ?? {},
86
+ });
87
+ continue;
88
+ }
89
+ if (isFunctionResponsePart(part)) {
90
+ const response = part.functionResponse.response;
91
+ const content = stringifyResponse(response);
92
+ out.push({
93
+ kind: 'tool-result',
94
+ timestamp: ts,
95
+ name: part.functionResponse.name,
96
+ content,
97
+ isError: false,
98
+ });
99
+ continue;
100
+ }
101
+ }
102
+ return out;
103
+ }
104
+ }
105
+ function isTextPart(part) {
106
+ return (typeof part === 'object' &&
107
+ part !== null &&
108
+ typeof part.text === 'string');
109
+ }
110
+ function isFunctionCallPart(part) {
111
+ if (typeof part !== 'object' || part === null)
112
+ return false;
113
+ const fc = part.functionCall;
114
+ return Boolean(fc) && typeof fc.name === 'string';
115
+ }
116
+ function isFunctionResponsePart(part) {
117
+ if (typeof part !== 'object' || part === null)
118
+ return false;
119
+ const fr = part.functionResponse;
120
+ return Boolean(fr) && typeof fr.name === 'string';
121
+ }
122
+ function stringifyResponse(response) {
123
+ if (response == null)
124
+ return '';
125
+ if (typeof response === 'string')
126
+ return response;
127
+ // Gemini wraps tool output as { response: { content: '...' } } commonly;
128
+ // unwrap a single-string `content` field for readability, otherwise
129
+ // round-trip the JSON.
130
+ if (typeof response === 'object' &&
131
+ !Array.isArray(response) &&
132
+ 'content' in response &&
133
+ typeof response.content === 'string') {
134
+ return response.content;
135
+ }
136
+ return JSON.stringify(response);
137
+ }
@@ -0,0 +1,7 @@
1
+ export * from './aider-history.js';
2
+ export * from './claude-code-jsonl.js';
3
+ export * from './codex-session.js';
4
+ export * from './continue-devdata.js';
5
+ export * from './cursor-sqlite.js';
6
+ export * from './gemini-session.js';
7
+ export * from './types.js';
@@ -0,0 +1,7 @@
1
+ export * from './aider-history.js';
2
+ export * from './claude-code-jsonl.js';
3
+ export * from './codex-session.js';
4
+ export * from './continue-devdata.js';
5
+ export * from './cursor-sqlite.js';
6
+ export * from './gemini-session.js';
7
+ export * from './types.js';
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod/v3';
2
+ export declare const TranscriptEventSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
3
+ kind: z.ZodLiteral<"user-message">;
4
+ timestamp: z.ZodString;
5
+ content: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ kind: "user-message";
8
+ content: string;
9
+ timestamp: string;
10
+ }, {
11
+ kind: "user-message";
12
+ content: string;
13
+ timestamp: string;
14
+ }>, z.ZodObject<{
15
+ kind: z.ZodLiteral<"assistant-message">;
16
+ timestamp: z.ZodString;
17
+ content: z.ZodString;
18
+ }, "strip", z.ZodTypeAny, {
19
+ kind: "assistant-message";
20
+ content: string;
21
+ timestamp: string;
22
+ }, {
23
+ kind: "assistant-message";
24
+ content: string;
25
+ timestamp: string;
26
+ }>, z.ZodObject<{
27
+ kind: z.ZodLiteral<"tool-call">;
28
+ timestamp: z.ZodString;
29
+ name: z.ZodString;
30
+ args: z.ZodRecord<z.ZodString, z.ZodUnknown>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ name: string;
33
+ kind: "tool-call";
34
+ timestamp: string;
35
+ args: Record<string, unknown>;
36
+ }, {
37
+ name: string;
38
+ kind: "tool-call";
39
+ timestamp: string;
40
+ args: Record<string, unknown>;
41
+ }>, z.ZodObject<{
42
+ kind: z.ZodLiteral<"tool-result">;
43
+ timestamp: z.ZodString;
44
+ name: z.ZodString;
45
+ content: z.ZodString;
46
+ isError: z.ZodOptional<z.ZodBoolean>;
47
+ }, "strip", z.ZodTypeAny, {
48
+ name: string;
49
+ kind: "tool-result";
50
+ content: string;
51
+ timestamp: string;
52
+ isError?: boolean | undefined;
53
+ }, {
54
+ name: string;
55
+ kind: "tool-result";
56
+ content: string;
57
+ timestamp: string;
58
+ isError?: boolean | undefined;
59
+ }>, z.ZodObject<{
60
+ kind: z.ZodLiteral<"edit">;
61
+ timestamp: z.ZodString;
62
+ path: z.ZodString;
63
+ diff: z.ZodString;
64
+ }, "strip", z.ZodTypeAny, {
65
+ path: string;
66
+ kind: "edit";
67
+ timestamp: string;
68
+ diff: string;
69
+ }, {
70
+ path: string;
71
+ kind: "edit";
72
+ timestamp: string;
73
+ diff: string;
74
+ }>]>;
75
+ export type TranscriptEvent = z.infer<typeof TranscriptEventSchema>;
76
+ /**
77
+ * Per-tool transcript parser. Each implementation reads the native disk format
78
+ * for one tool (Claude Code JSONL, Cursor SQLite, Aider history, etc.) and
79
+ * normalises into the canonical TranscriptEvent shape so the synthetic-span
80
+ * builder (Task 3.2) can produce uniform OTel spans regardless of source.
81
+ */
82
+ export interface TranscriptParser {
83
+ /** Tool identity. Used by the synthetic-span builder to brand `service.name`. */
84
+ readonly tool: 'claude-code' | 'codex' | 'gemini-cli' | 'cursor' | 'aider' | 'continue';
85
+ /**
86
+ * Incremental parser. Called when the watcher observes new bytes appended
87
+ * to the transcript file. Returns events drained since the last call.
88
+ */
89
+ parseChunk(input: {
90
+ sessionId: string;
91
+ chunk: Buffer | string;
92
+ }): TranscriptEvent[];
93
+ /**
94
+ * One-shot parser for a complete transcript file. Used by the
95
+ * `jinn capture import` CLI and as the path-D safety-net ingest at session end.
96
+ */
97
+ parseFull(input: {
98
+ sessionId: string;
99
+ path: string;
100
+ }): Promise<TranscriptEvent[]>;
101
+ }
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod/v3';
2
+ export const TranscriptEventSchema = z.discriminatedUnion('kind', [
3
+ z.object({
4
+ kind: z.literal('user-message'),
5
+ timestamp: z.string().datetime(),
6
+ content: z.string(),
7
+ }),
8
+ z.object({
9
+ kind: z.literal('assistant-message'),
10
+ timestamp: z.string().datetime(),
11
+ content: z.string(),
12
+ }),
13
+ z.object({
14
+ kind: z.literal('tool-call'),
15
+ timestamp: z.string().datetime(),
16
+ name: z.string(),
17
+ args: z.record(z.unknown()),
18
+ }),
19
+ z.object({
20
+ kind: z.literal('tool-result'),
21
+ timestamp: z.string().datetime(),
22
+ name: z.string(),
23
+ content: z.string(),
24
+ isError: z.boolean().optional(),
25
+ }),
26
+ z.object({
27
+ kind: z.literal('edit'),
28
+ timestamp: z.string().datetime(),
29
+ path: z.string(),
30
+ diff: z.string(),
31
+ }),
32
+ ]);
@@ -0,0 +1,4 @@
1
+ export declare function truncate(value: string): string;
2
+ export declare function truncateLeaves(value: unknown): unknown;
3
+ export declare function stringifyResultContent(content: unknown): string;
4
+ export declare function makeProvenanceAttrs(sourceFormat: string, parserName: string, parserVersion: string): Record<string, unknown>;
@@ -0,0 +1,30 @@
1
+ const MAX_ATTR_LENGTH = 8000;
2
+ export function truncate(value) {
3
+ return value.length > MAX_ATTR_LENGTH ? value.slice(0, MAX_ATTR_LENGTH) : value;
4
+ }
5
+ export function truncateLeaves(value) {
6
+ if (typeof value === 'string')
7
+ return truncate(value);
8
+ if (Array.isArray(value))
9
+ return value.map(truncateLeaves);
10
+ if (value && typeof value === 'object') {
11
+ const result = {};
12
+ for (const [key, child] of Object.entries(value)) {
13
+ result[key] = truncateLeaves(child);
14
+ }
15
+ return result;
16
+ }
17
+ return value;
18
+ }
19
+ export function stringifyResultContent(content) {
20
+ if (typeof content === 'string')
21
+ return content;
22
+ return JSON.stringify(content) ?? String(content);
23
+ }
24
+ export function makeProvenanceAttrs(sourceFormat, parserName, parserVersion) {
25
+ return {
26
+ 'jinn.transcript.sourceFormat': sourceFormat,
27
+ 'jinn.transcript.parser': parserName,
28
+ 'jinn.transcript.parserVersion': parserVersion,
29
+ };
30
+ }
@@ -0,0 +1,8 @@
1
+ import type { TranscriptSpanInput, TranscriptSpanParser } from './types.js';
2
+ export declare class ClaudeCodeStreamJsonParser implements TranscriptSpanParser {
3
+ readonly sourceFormat = "claude-code-stream-json";
4
+ readonly parserName = "claude-code-stream-json";
5
+ readonly parserVersion = "1.0.0";
6
+ parse(transcriptPath: string): Promise<TranscriptSpanInput[]>;
7
+ parseText(rawText: string): TranscriptSpanInput[];
8
+ }
@@ -0,0 +1,162 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { makeProvenanceAttrs, stringifyResultContent, truncate, truncateLeaves, } from './attrs.js';
3
+ const SOURCE_FORMAT = 'claude-code-stream-json';
4
+ const PARSER_NAME = 'claude-code-stream-json';
5
+ const PARSER_VERSION = '1.0.0';
6
+ function provenanceAttrs() {
7
+ return makeProvenanceAttrs(SOURCE_FORMAT, PARSER_NAME, PARSER_VERSION);
8
+ }
9
+ export class ClaudeCodeStreamJsonParser {
10
+ sourceFormat = SOURCE_FORMAT;
11
+ parserName = PARSER_NAME;
12
+ parserVersion = PARSER_VERSION;
13
+ async parse(transcriptPath) {
14
+ let rawText;
15
+ try {
16
+ rawText = await readFile(transcriptPath, 'utf-8');
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ return this.parseText(rawText);
22
+ }
23
+ parseText(rawText) {
24
+ const spans = [];
25
+ const pendingByToolUseId = new Map();
26
+ const agentTurns = [];
27
+ let nextNs = BigInt(Date.now()) * 1000000n;
28
+ const nextTimestamp = () => {
29
+ const timestamp = nextNs;
30
+ nextNs += 1n;
31
+ return String(timestamp);
32
+ };
33
+ for (const line of rawText.split('\n')) {
34
+ const trimmed = line.trim();
35
+ if (!trimmed)
36
+ continue;
37
+ let record;
38
+ try {
39
+ record = JSON.parse(trimmed);
40
+ }
41
+ catch {
42
+ continue;
43
+ }
44
+ if (!record || typeof record.type !== 'string' || record.type === 'system')
45
+ continue;
46
+ if (record.type === 'result') {
47
+ const inputTokens = record.usage?.input_tokens;
48
+ const outputTokens = record.usage?.output_tokens;
49
+ if (typeof inputTokens === 'number' &&
50
+ typeof outputTokens === 'number' &&
51
+ agentTurns.length > 0) {
52
+ const last = agentTurns[agentTurns.length - 1];
53
+ last.attributes['gen_ai.usage.input_tokens'] = inputTokens;
54
+ last.attributes['gen_ai.usage.output_tokens'] = outputTokens;
55
+ }
56
+ continue;
57
+ }
58
+ if (record.type === 'user') {
59
+ const content = record.message?.content;
60
+ if (typeof content === 'string') {
61
+ const timestamp = nextTimestamp();
62
+ const turn = {
63
+ name: 'agent_turn.user',
64
+ kind: 'INTERNAL',
65
+ startTimeUnixNano: timestamp,
66
+ endTimeUnixNano: timestamp,
67
+ attributes: {
68
+ 'jinn.span.kind': 'jinn.agent_turn',
69
+ 'jinn.turn.role': 'user',
70
+ 'message.content': truncate(content),
71
+ ...provenanceAttrs(),
72
+ },
73
+ events: [],
74
+ status: { code: 'OK' },
75
+ };
76
+ spans.push(turn);
77
+ agentTurns.push(turn);
78
+ continue;
79
+ }
80
+ if (Array.isArray(content)) {
81
+ for (const block of content) {
82
+ if (!block ||
83
+ typeof block !== 'object' ||
84
+ block.type !== 'tool_result') {
85
+ continue;
86
+ }
87
+ const toolResult = block;
88
+ const call = pendingByToolUseId.get(toolResult.tool_use_id);
89
+ if (!call)
90
+ continue;
91
+ pendingByToolUseId.delete(toolResult.tool_use_id);
92
+ const timestamp = nextTimestamp();
93
+ call.endTimeUnixNano = timestamp;
94
+ call.events = [
95
+ {
96
+ timeUnixNano: timestamp,
97
+ name: 'tool_result',
98
+ attributes: {
99
+ 'tool.result': truncate(stringifyResultContent(toolResult.content)),
100
+ 'tool.result.is_error': toolResult.is_error ?? false,
101
+ },
102
+ },
103
+ ];
104
+ call.status = toolResult.is_error ? { code: 'ERROR' } : { code: 'OK' };
105
+ }
106
+ }
107
+ continue;
108
+ }
109
+ if (record.type !== 'assistant' || !Array.isArray(record.message?.content))
110
+ continue;
111
+ for (const block of record.message.content) {
112
+ if (!block || typeof block !== 'object')
113
+ continue;
114
+ const blockType = block.type;
115
+ if (blockType === 'thinking')
116
+ continue;
117
+ if (blockType === 'text') {
118
+ const text = block;
119
+ const timestamp = nextTimestamp();
120
+ const turn = {
121
+ name: 'agent_turn.assistant',
122
+ kind: 'INTERNAL',
123
+ startTimeUnixNano: timestamp,
124
+ endTimeUnixNano: timestamp,
125
+ attributes: {
126
+ 'jinn.span.kind': 'jinn.agent_turn',
127
+ 'jinn.turn.role': 'assistant',
128
+ 'message.content': truncate(text.text),
129
+ ...provenanceAttrs(),
130
+ },
131
+ events: [],
132
+ status: { code: 'OK' },
133
+ };
134
+ spans.push(turn);
135
+ agentTurns.push(turn);
136
+ continue;
137
+ }
138
+ if (blockType === 'tool_use') {
139
+ const toolUse = block;
140
+ const timestamp = nextTimestamp();
141
+ const call = {
142
+ name: `tool_call.${toolUse.name}`,
143
+ kind: 'INTERNAL',
144
+ startTimeUnixNano: timestamp,
145
+ endTimeUnixNano: timestamp,
146
+ attributes: {
147
+ 'jinn.span.kind': 'jinn.tool_call',
148
+ 'tool.name': toolUse.name,
149
+ 'tool.args': truncateLeaves(toolUse.input),
150
+ ...provenanceAttrs(),
151
+ },
152
+ events: [],
153
+ status: { code: 'UNSET' },
154
+ };
155
+ spans.push(call);
156
+ pendingByToolUseId.set(toolUse.id, call);
157
+ }
158
+ }
159
+ }
160
+ return spans;
161
+ }
162
+ }
@@ -0,0 +1,8 @@
1
+ import type { TranscriptSpanInput, TranscriptSpanParser } from './types.js';
2
+ export declare class CodexExecJsonParser implements TranscriptSpanParser {
3
+ readonly sourceFormat = "codex-exec-json";
4
+ readonly parserName = "codex-exec-json";
5
+ readonly parserVersion = "1.1.0";
6
+ parse(transcriptPath: string): Promise<TranscriptSpanInput[]>;
7
+ parseText(rawText: string): TranscriptSpanInput[];
8
+ }