@nothumanwork/nn 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.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/bin/nn.js +106 -0
- package/package.json +74 -0
- package/src/config/env.ts +31 -0
- package/src/config/paths.ts +50 -0
- package/src/config/runtime.ts +37 -0
- package/src/config/sync.ts +48 -0
- package/src/db/client.ts +333 -0
- package/src/db/libsql-native.ts +66 -0
- package/src/db/lock.ts +72 -0
- package/src/db/migrate.ts +246 -0
- package/src/db/replica-migrate.ts +162 -0
- package/src/db/schema.sql +99 -0
- package/src/export/claude.ts +92 -0
- package/src/export/codex.ts +86 -0
- package/src/export/cursor.ts +68 -0
- package/src/export/generic.ts +19 -0
- package/src/export/registry.ts +118 -0
- package/src/export/types.ts +44 -0
- package/src/hooks/ingest.ts +107 -0
- package/src/hooks/resolvers/antigravity.ts +44 -0
- package/src/hooks/resolvers/claude.ts +27 -0
- package/src/hooks/resolvers/codex.ts +65 -0
- package/src/hooks/resolvers/common.ts +21 -0
- package/src/hooks/resolvers/cursor.ts +31 -0
- package/src/hooks/resolvers/grok.ts +59 -0
- package/src/hooks/resolvers/index.ts +35 -0
- package/src/hooks/resolvers/pi.ts +72 -0
- package/src/hooks/types.ts +20 -0
- package/src/index.ts +247 -0
- package/src/ingest/jsonl.ts +38 -0
- package/src/ingest/pipeline.ts +101 -0
- package/src/install/index.ts +227 -0
- package/src/install/types.ts +85 -0
- package/src/ir/event-id.ts +26 -0
- package/src/ir/types.ts +84 -0
- package/src/providers/antigravity/index.ts +175 -0
- package/src/providers/claude/index.ts +228 -0
- package/src/providers/codex/index.ts +264 -0
- package/src/providers/copilot/index.ts +24 -0
- package/src/providers/cursor/index.ts +340 -0
- package/src/providers/grok/index.ts +146 -0
- package/src/providers/pi/index.ts +197 -0
- package/src/providers/registry.ts +31 -0
- package/src/providers/types.ts +53 -0
- package/src/sync/coordinator.ts +186 -0
- package/src/sync/turso.ts +64 -0
- package/src/types/assets.d.ts +4 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { NormalizedEvent } from "./types.ts";
|
|
4
|
+
|
|
5
|
+
export function contentHash(text: string): string {
|
|
6
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function eventIdFor(
|
|
10
|
+
event: Pick<NormalizedEvent, "source" | "sourceSessionId" | "lineKey">,
|
|
11
|
+
): string {
|
|
12
|
+
const material = `${event.source}|${event.sourceSessionId}|${event.lineKey}`;
|
|
13
|
+
return `evt:v1:${contentHash(material)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function projectIdFor(canonicalPath: string): string {
|
|
17
|
+
return `proj:v1:${contentHash(canonicalPath)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function sessionIdFor(
|
|
21
|
+
projectId: string,
|
|
22
|
+
source: string,
|
|
23
|
+
sourceSessionId: string,
|
|
24
|
+
): string {
|
|
25
|
+
return `sess:v1:${contentHash(`${projectId}|${source}|${sourceSessionId}`)}`;
|
|
26
|
+
}
|
package/src/ir/types.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const PROVIDERS = [
|
|
2
|
+
"cursor",
|
|
3
|
+
"claude",
|
|
4
|
+
"codex",
|
|
5
|
+
"copilot",
|
|
6
|
+
"antigravity",
|
|
7
|
+
"pi",
|
|
8
|
+
"grok",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
export type ProviderId = (typeof PROVIDERS)[number];
|
|
12
|
+
|
|
13
|
+
export const EVENT_BOUNDARIES = [
|
|
14
|
+
"session_start",
|
|
15
|
+
"session_end",
|
|
16
|
+
"user_turn",
|
|
17
|
+
"assistant_turn",
|
|
18
|
+
"tool_call",
|
|
19
|
+
"tool_result",
|
|
20
|
+
"compaction",
|
|
21
|
+
"unknown_raw_event",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
export type EventBoundary = (typeof EVENT_BOUNDARIES)[number];
|
|
25
|
+
|
|
26
|
+
export function defaultRole(boundary: EventBoundary): string {
|
|
27
|
+
switch (boundary) {
|
|
28
|
+
case "session_start":
|
|
29
|
+
case "session_end":
|
|
30
|
+
case "compaction":
|
|
31
|
+
case "unknown_raw_event":
|
|
32
|
+
return "system";
|
|
33
|
+
case "user_turn":
|
|
34
|
+
return "user";
|
|
35
|
+
case "assistant_turn":
|
|
36
|
+
case "tool_call":
|
|
37
|
+
return "assistant";
|
|
38
|
+
case "tool_result":
|
|
39
|
+
return "tool";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface NormalizedEvent {
|
|
44
|
+
source: ProviderId;
|
|
45
|
+
sourceSessionId: string;
|
|
46
|
+
sourceEventId?: string;
|
|
47
|
+
lineKey: string;
|
|
48
|
+
boundary: EventBoundary;
|
|
49
|
+
role: string;
|
|
50
|
+
timestamp: string;
|
|
51
|
+
contentText: string;
|
|
52
|
+
toolName?: string;
|
|
53
|
+
toolCallId?: string;
|
|
54
|
+
toolInputJson?: string;
|
|
55
|
+
toolOutputJson?: string;
|
|
56
|
+
parserVersion: string;
|
|
57
|
+
rawLocalRef: string;
|
|
58
|
+
rawJson: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SourceSessionRef {
|
|
62
|
+
source: ProviderId;
|
|
63
|
+
sessionId: string;
|
|
64
|
+
path: string;
|
|
65
|
+
workspaceRoot?: string;
|
|
66
|
+
title?: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface SourceImportBatch {
|
|
71
|
+
source: ProviderId;
|
|
72
|
+
parserVersion: string;
|
|
73
|
+
session: SourceSessionRef;
|
|
74
|
+
events: NormalizedEvent[];
|
|
75
|
+
cursorKey: string;
|
|
76
|
+
consumedBytes: number;
|
|
77
|
+
emittedLines: number;
|
|
78
|
+
warnings: string[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ExportWarning {
|
|
82
|
+
code: string;
|
|
83
|
+
message: string;
|
|
84
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { homeDir, projectDir } from "../../config/paths.ts";
|
|
5
|
+
import { lineKeyFor, parseCompleteJsonlLines } from "../../ingest/jsonl.ts";
|
|
6
|
+
import type {
|
|
7
|
+
EventBoundary,
|
|
8
|
+
NormalizedEvent,
|
|
9
|
+
SourceImportBatch,
|
|
10
|
+
SourceSessionRef,
|
|
11
|
+
} from "../../ir/types.ts";
|
|
12
|
+
import { defaultRole } from "../../ir/types.ts";
|
|
13
|
+
import type { ProviderAdapter } from "../types.ts";
|
|
14
|
+
import { stringField, truncate } from "../types.ts";
|
|
15
|
+
|
|
16
|
+
export const PARSER_VERSION = "antigravity-step-jsonl-v1";
|
|
17
|
+
|
|
18
|
+
function antigravityRoot(): string {
|
|
19
|
+
return join(homeDir(), ".gemini", "antigravity-cli", "brain");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function discoverAntigravitySessions(): SourceSessionRef[] {
|
|
23
|
+
if (!existsSync(antigravityRoot())) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const sessions: SourceSessionRef[] = [];
|
|
27
|
+
for (const brainEntry of readdirSync(antigravityRoot(), {
|
|
28
|
+
withFileTypes: true,
|
|
29
|
+
})) {
|
|
30
|
+
if (!brainEntry.isDirectory()) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const transcriptPath = join(
|
|
34
|
+
antigravityRoot(),
|
|
35
|
+
brainEntry.name,
|
|
36
|
+
".system_generated",
|
|
37
|
+
"logs",
|
|
38
|
+
"transcript.jsonl",
|
|
39
|
+
);
|
|
40
|
+
if (!existsSync(transcriptPath)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
sessions.push({
|
|
44
|
+
source: "antigravity",
|
|
45
|
+
sessionId: brainEntry.name,
|
|
46
|
+
path: transcriptPath,
|
|
47
|
+
workspaceRoot: projectDir(),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return sessions;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function boundaryForStep(source: string, stepType: string): EventBoundary {
|
|
54
|
+
if (source === "USER_EXPLICIT" || stepType === "USER_INPUT") {
|
|
55
|
+
return "user_turn";
|
|
56
|
+
}
|
|
57
|
+
if (stepType === "PLANNER_RESPONSE") {
|
|
58
|
+
return "assistant_turn";
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
stepType.includes("SEARCH") ||
|
|
62
|
+
stepType.includes("VIEW") ||
|
|
63
|
+
stepType.includes("LIST")
|
|
64
|
+
) {
|
|
65
|
+
return "tool_call";
|
|
66
|
+
}
|
|
67
|
+
return "unknown_raw_event";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseAntigravityFile(
|
|
71
|
+
sourcePath: string,
|
|
72
|
+
sessionId: string,
|
|
73
|
+
offset: number,
|
|
74
|
+
): SourceImportBatch {
|
|
75
|
+
const buffer = readFileSync(sourcePath);
|
|
76
|
+
const slice = buffer.subarray(offset);
|
|
77
|
+
const { lines, consumedBytes, partialTail } = parseCompleteJsonlLines(
|
|
78
|
+
slice,
|
|
79
|
+
0,
|
|
80
|
+
);
|
|
81
|
+
const events: NormalizedEvent[] = [];
|
|
82
|
+
const warnings: string[] = [];
|
|
83
|
+
let emittedLines = 0;
|
|
84
|
+
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
emittedLines = line.lineIndex;
|
|
87
|
+
const absoluteStart = offset + line.lineStart;
|
|
88
|
+
const lineKey = lineKeyFor(sourcePath, absoluteStart);
|
|
89
|
+
let value: Record<string, unknown>;
|
|
90
|
+
try {
|
|
91
|
+
value = JSON.parse(line.raw) as Record<string, unknown>;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
warnings.push(
|
|
94
|
+
`${sourcePath}:${line.lineIndex} malformed row: ${String(error)}`,
|
|
95
|
+
);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const source = stringField(value, "source") ?? "SYSTEM";
|
|
100
|
+
const stepType = stringField(value, "type") ?? "unknown";
|
|
101
|
+
const timestamp =
|
|
102
|
+
stringField(value, "created_at") ?? new Date().toISOString();
|
|
103
|
+
const content = stringField(value, "content") ?? "";
|
|
104
|
+
const boundary = boundaryForStep(source, stepType);
|
|
105
|
+
const toolCalls = Array.isArray(value.tool_calls) ? value.tool_calls : [];
|
|
106
|
+
|
|
107
|
+
events.push({
|
|
108
|
+
source: "antigravity",
|
|
109
|
+
sourceSessionId: sessionId,
|
|
110
|
+
sourceEventId: `step:${String(value.step_index ?? line.lineIndex)}`,
|
|
111
|
+
lineKey,
|
|
112
|
+
boundary,
|
|
113
|
+
role:
|
|
114
|
+
boundary === "user_turn" ? "user"
|
|
115
|
+
: boundary === "assistant_turn" ? "assistant"
|
|
116
|
+
: defaultRole(boundary),
|
|
117
|
+
timestamp,
|
|
118
|
+
contentText: truncate(content || stepType),
|
|
119
|
+
parserVersion: PARSER_VERSION,
|
|
120
|
+
rawLocalRef: `${sourcePath}:${line.lineIndex}`,
|
|
121
|
+
rawJson: line.raw,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
for (const [index, call] of toolCalls.entries()) {
|
|
125
|
+
const item = call as Record<string, unknown>;
|
|
126
|
+
events.push({
|
|
127
|
+
source: "antigravity",
|
|
128
|
+
sourceSessionId: sessionId,
|
|
129
|
+
sourceEventId: `step:${String(value.step_index ?? line.lineIndex)}:tool:${index}`,
|
|
130
|
+
lineKey: `${lineKey}:tool:${index}`,
|
|
131
|
+
boundary: "tool_call",
|
|
132
|
+
role: "assistant",
|
|
133
|
+
timestamp,
|
|
134
|
+
contentText: truncate(
|
|
135
|
+
`${stringField(item, "name") ?? "tool"} ${JSON.stringify(item.args ?? {})}`,
|
|
136
|
+
),
|
|
137
|
+
toolName: stringField(item, "name") ?? undefined,
|
|
138
|
+
toolInputJson: JSON.stringify(item.args ?? {}),
|
|
139
|
+
parserVersion: PARSER_VERSION,
|
|
140
|
+
rawLocalRef: `${sourcePath}:${line.lineIndex}`,
|
|
141
|
+
rawJson: JSON.stringify(item),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const absoluteConsumed =
|
|
147
|
+
partialTail ?
|
|
148
|
+
offset + (lines.at(-1)?.lineEnd ?? 0)
|
|
149
|
+
: offset + consumedBytes;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
source: "antigravity",
|
|
153
|
+
parserVersion: PARSER_VERSION,
|
|
154
|
+
session: {
|
|
155
|
+
source: "antigravity",
|
|
156
|
+
sessionId,
|
|
157
|
+
path: sourcePath,
|
|
158
|
+
workspaceRoot: projectDir(),
|
|
159
|
+
},
|
|
160
|
+
events,
|
|
161
|
+
cursorKey: sourcePath,
|
|
162
|
+
consumedBytes: absoluteConsumed,
|
|
163
|
+
emittedLines,
|
|
164
|
+
warnings,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const antigravityProvider: ProviderAdapter = {
|
|
169
|
+
name: "antigravity",
|
|
170
|
+
parserVersion: PARSER_VERSION,
|
|
171
|
+
discover: discoverAntigravitySessions,
|
|
172
|
+
parseIncremental: parseAntigravityFile,
|
|
173
|
+
parseFull: (sourcePath, sessionId) =>
|
|
174
|
+
parseAntigravityFile(sourcePath, sessionId, 0),
|
|
175
|
+
};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { homeDir, encodeProjectSlug, projectDir } from "../../config/paths.ts";
|
|
5
|
+
import { lineKeyFor, parseCompleteJsonlLines } from "../../ingest/jsonl.ts";
|
|
6
|
+
import type {
|
|
7
|
+
EventBoundary,
|
|
8
|
+
NormalizedEvent,
|
|
9
|
+
SourceImportBatch,
|
|
10
|
+
SourceSessionRef,
|
|
11
|
+
} from "../../ir/types.ts";
|
|
12
|
+
import { defaultRole } from "../../ir/types.ts";
|
|
13
|
+
import type { ProviderAdapter } from "../types.ts";
|
|
14
|
+
import { stringField, textFromUnknown, truncate } from "../types.ts";
|
|
15
|
+
|
|
16
|
+
export const PARSER_VERSION = "claude-code-jsonl-v1";
|
|
17
|
+
|
|
18
|
+
function claudeRoot(): string {
|
|
19
|
+
return join(homeDir(), ".claude", "projects");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function discoverClaudeSessions(): SourceSessionRef[] {
|
|
23
|
+
if (!existsSync(claudeRoot())) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const slug = encodeProjectSlug(projectDir());
|
|
27
|
+
const sessions: SourceSessionRef[] = [];
|
|
28
|
+
for (const projectEntry of readdirSync(claudeRoot(), {
|
|
29
|
+
withFileTypes: true,
|
|
30
|
+
})) {
|
|
31
|
+
if (!projectEntry.isDirectory() || projectEntry.name !== slug) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const projectPath = join(claudeRoot(), projectEntry.name);
|
|
35
|
+
for (const entry of readdirSync(projectPath, { withFileTypes: true })) {
|
|
36
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const path = join(projectPath, entry.name);
|
|
40
|
+
sessions.push({
|
|
41
|
+
source: "claude",
|
|
42
|
+
sessionId: basename(entry.name, ".jsonl"),
|
|
43
|
+
path,
|
|
44
|
+
workspaceRoot: projectDir(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return sessions;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function claudeBoundaryForBlock(
|
|
52
|
+
blockType: string,
|
|
53
|
+
rowType: string,
|
|
54
|
+
): EventBoundary {
|
|
55
|
+
if (rowType === "system") {
|
|
56
|
+
return "session_start";
|
|
57
|
+
}
|
|
58
|
+
if (blockType === "tool_use") {
|
|
59
|
+
return "tool_call";
|
|
60
|
+
}
|
|
61
|
+
if (blockType === "tool_result") {
|
|
62
|
+
return "tool_result";
|
|
63
|
+
}
|
|
64
|
+
if (blockType === "thinking") {
|
|
65
|
+
return "compaction";
|
|
66
|
+
}
|
|
67
|
+
if (rowType === "user") {
|
|
68
|
+
return "user_turn";
|
|
69
|
+
}
|
|
70
|
+
if (rowType === "assistant") {
|
|
71
|
+
return "assistant_turn";
|
|
72
|
+
}
|
|
73
|
+
return "unknown_raw_event";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseClaudeFile(
|
|
77
|
+
sourcePath: string,
|
|
78
|
+
sessionId: string,
|
|
79
|
+
offset: number,
|
|
80
|
+
): SourceImportBatch {
|
|
81
|
+
const buffer = readFileSync(sourcePath);
|
|
82
|
+
const slice = buffer.subarray(offset);
|
|
83
|
+
const { lines, consumedBytes, partialTail } = parseCompleteJsonlLines(
|
|
84
|
+
slice,
|
|
85
|
+
0,
|
|
86
|
+
);
|
|
87
|
+
const events: NormalizedEvent[] = [];
|
|
88
|
+
const warnings: string[] = [];
|
|
89
|
+
let currentSessionId = sessionId;
|
|
90
|
+
let emittedLines = 0;
|
|
91
|
+
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
emittedLines = line.lineIndex;
|
|
94
|
+
const absoluteStart = offset + line.lineStart;
|
|
95
|
+
const lineKey = lineKeyFor(sourcePath, absoluteStart);
|
|
96
|
+
let value: Record<string, unknown>;
|
|
97
|
+
try {
|
|
98
|
+
value = JSON.parse(line.raw) as Record<string, unknown>;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
warnings.push(
|
|
101
|
+
`${sourcePath}:${line.lineIndex} malformed row: ${String(error)}`,
|
|
102
|
+
);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const rowType = stringField(value, "type") ?? "unknown";
|
|
107
|
+
if (
|
|
108
|
+
["file-history-snapshot", "attachment", "permission-mode"].includes(
|
|
109
|
+
rowType,
|
|
110
|
+
)
|
|
111
|
+
) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
currentSessionId = stringField(value, "sessionId") ?? currentSessionId;
|
|
116
|
+
const timestamp =
|
|
117
|
+
stringField(value, "timestamp") ?? new Date().toISOString();
|
|
118
|
+
const rowEventId = stringField(value, "uuid") ?? `line:${line.lineIndex}`;
|
|
119
|
+
const message = value.message;
|
|
120
|
+
const content =
|
|
121
|
+
typeof message === "object" && message !== null ?
|
|
122
|
+
(message as Record<string, unknown>).content
|
|
123
|
+
: undefined;
|
|
124
|
+
|
|
125
|
+
if (typeof content === "string") {
|
|
126
|
+
events.push({
|
|
127
|
+
source: "claude",
|
|
128
|
+
sourceSessionId: currentSessionId,
|
|
129
|
+
sourceEventId: rowEventId,
|
|
130
|
+
lineKey,
|
|
131
|
+
boundary:
|
|
132
|
+
rowType === "user" ? "user_turn"
|
|
133
|
+
: rowType === "assistant" ? "assistant_turn"
|
|
134
|
+
: "unknown_raw_event",
|
|
135
|
+
role: rowType,
|
|
136
|
+
timestamp,
|
|
137
|
+
contentText: content,
|
|
138
|
+
parserVersion: PARSER_VERSION,
|
|
139
|
+
rawLocalRef: `${sourcePath}:${line.lineIndex}`,
|
|
140
|
+
rawJson: line.raw,
|
|
141
|
+
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (Array.isArray(content)) {
|
|
146
|
+
for (const [index, block] of content.entries()) {
|
|
147
|
+
const item = block as Record<string, unknown>;
|
|
148
|
+
const blockType = stringField(item, "type") ?? "text";
|
|
149
|
+
const text = textFromUnknown(item) ?? "";
|
|
150
|
+
if (!text.trim() && blockType === "text") {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const boundary = claudeBoundaryForBlock(blockType, rowType);
|
|
154
|
+
events.push({
|
|
155
|
+
source: "claude",
|
|
156
|
+
sourceSessionId: currentSessionId,
|
|
157
|
+
sourceEventId: `${rowEventId}:block:${index}`,
|
|
158
|
+
lineKey: `${lineKey}:block:${index}`,
|
|
159
|
+
boundary,
|
|
160
|
+
role: boundary === "tool_result" ? "tool" : rowType,
|
|
161
|
+
timestamp,
|
|
162
|
+
contentText: truncate(text || JSON.stringify(item)),
|
|
163
|
+
toolName: stringField(item, "name"),
|
|
164
|
+
toolCallId:
|
|
165
|
+
stringField(item, "id") ?? stringField(item, "tool_use_id"),
|
|
166
|
+
toolInputJson:
|
|
167
|
+
blockType === "tool_use" ?
|
|
168
|
+
JSON.stringify(item.input ?? {})
|
|
169
|
+
: undefined,
|
|
170
|
+
toolOutputJson:
|
|
171
|
+
blockType === "tool_result" ?
|
|
172
|
+
JSON.stringify(item.content ?? item)
|
|
173
|
+
: undefined,
|
|
174
|
+
parserVersion: PARSER_VERSION,
|
|
175
|
+
rawLocalRef: `${sourcePath}:${line.lineIndex}`,
|
|
176
|
+
rawJson: line.raw,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const fallback =
|
|
183
|
+
textFromUnknown(message) ?? truncate(JSON.stringify(value));
|
|
184
|
+
events.push({
|
|
185
|
+
source: "claude",
|
|
186
|
+
sourceSessionId: currentSessionId,
|
|
187
|
+
sourceEventId: rowEventId,
|
|
188
|
+
lineKey,
|
|
189
|
+
boundary: "unknown_raw_event",
|
|
190
|
+
role: defaultRole("unknown_raw_event"),
|
|
191
|
+
timestamp,
|
|
192
|
+
contentText: fallback,
|
|
193
|
+
parserVersion: PARSER_VERSION,
|
|
194
|
+
rawLocalRef: `${sourcePath}:${line.lineIndex}`,
|
|
195
|
+
rawJson: line.raw,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const absoluteConsumed =
|
|
200
|
+
partialTail ?
|
|
201
|
+
offset + (lines.at(-1)?.lineEnd ?? 0)
|
|
202
|
+
: offset + consumedBytes;
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
source: "claude",
|
|
206
|
+
parserVersion: PARSER_VERSION,
|
|
207
|
+
session: {
|
|
208
|
+
source: "claude",
|
|
209
|
+
sessionId: currentSessionId,
|
|
210
|
+
path: sourcePath,
|
|
211
|
+
workspaceRoot: projectDir(),
|
|
212
|
+
},
|
|
213
|
+
events,
|
|
214
|
+
cursorKey: sourcePath,
|
|
215
|
+
consumedBytes: absoluteConsumed,
|
|
216
|
+
emittedLines,
|
|
217
|
+
warnings,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const claudeProvider: ProviderAdapter = {
|
|
222
|
+
name: "claude",
|
|
223
|
+
parserVersion: PARSER_VERSION,
|
|
224
|
+
discover: discoverClaudeSessions,
|
|
225
|
+
parseIncremental: parseClaudeFile,
|
|
226
|
+
parseFull: (sourcePath, sessionId) =>
|
|
227
|
+
parseClaudeFile(sourcePath, sessionId, 0),
|
|
228
|
+
};
|