agentel 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -22
- package/docs/code-reference.md +19 -10
- package/docs/history-source-handling.md +116 -52
- package/docs/release.md +1 -1
- package/package.json +1 -1
- package/src/archive.js +1 -1
- package/src/canonical-events.js +74 -25
- package/src/cli.js +760 -93
- package/src/doctor.js +2 -0
- package/src/importers/claude.js +309 -11
- package/src/importers/providers.js +22 -0
- package/src/importers.js +403 -14
- package/src/parser-versions.js +1 -0
- package/src/search.js +1 -0
- package/src/sources.js +1 -0
- package/src/web-export-instructions.js +77 -0
package/src/importers.js
CHANGED
|
@@ -19,6 +19,7 @@ const { parseGeminiCliJsonSessions, parseGeminiCliJsonlSessions } = require("./i
|
|
|
19
19
|
const { importSourceWithAdapter, providerAdapterForSource, providerAdapterSources } = require("./importers/providers");
|
|
20
20
|
const { parseSince } = require("./importers/shared");
|
|
21
21
|
const { canonicalWebProvider, derivedAccountId, getWebAccount, upsertWebAccount } = require("./web-accounts");
|
|
22
|
+
const { manualImportInstructionResult } = require("./web-export-instructions");
|
|
22
23
|
|
|
23
24
|
const WEB_TOKEN_ESTIMATE_CHARS = 4;
|
|
24
25
|
const WEB_CHAT_TOKEN_ESTIMATION_METHOD = "web-message-parts-chars-v1";
|
|
@@ -46,6 +47,7 @@ function importProviderHelpers() {
|
|
|
46
47
|
importCursorProvider,
|
|
47
48
|
importJsonlProvider,
|
|
48
49
|
importStructuredProvider,
|
|
50
|
+
manualImportInstructionResult,
|
|
49
51
|
readAntigravitySessions,
|
|
50
52
|
readAiderSessions,
|
|
51
53
|
readClineSessions,
|
|
@@ -157,7 +159,7 @@ function importJsonlProvider(provider, roots, since, options = {}, env = process
|
|
|
157
159
|
sourceFiles: [item.file, sessionMetadata?.sourcePath || "", ...auxiliaryFiles].filter(Boolean),
|
|
158
160
|
sourceType,
|
|
159
161
|
title: jsonlSessionTitleForImport(parsed, sessionMetadata),
|
|
160
|
-
sessionSummary: claudeCodeSidecarSessionSummary(sessionMetadata)
|
|
162
|
+
sessionSummary: mergeSessionSummaries(claudeCodeSidecarSessionSummary(sessionMetadata), parsed.sessionSummary)
|
|
161
163
|
},
|
|
162
164
|
env
|
|
163
165
|
);
|
|
@@ -179,6 +181,12 @@ function jsonlProviderSourceType(provider) {
|
|
|
179
181
|
return "cli-history";
|
|
180
182
|
}
|
|
181
183
|
|
|
184
|
+
function codexThreadSourceType(thread) {
|
|
185
|
+
if (thread.source === "vscode") return "codex-desktop-history";
|
|
186
|
+
if (thread.source === "exec") return "codex-sdk-history";
|
|
187
|
+
return "codex-cli-history";
|
|
188
|
+
}
|
|
189
|
+
|
|
182
190
|
function importClaudeDesktopProvider(provider, since, options = {}, env = process.env) {
|
|
183
191
|
const state = loadImportState(env);
|
|
184
192
|
const archived = archivedSessionKeys(env);
|
|
@@ -257,10 +265,12 @@ function matchesImportedSessionRepo(session, repo, wantedRepos) {
|
|
|
257
265
|
|
|
258
266
|
function importCodexProvider(provider, since, options = {}, env = process.env) {
|
|
259
267
|
const threads = readCodexSessionEntries(env).filter((thread) => {
|
|
260
|
-
if (options.codexSource
|
|
261
|
-
|
|
262
|
-
return true;
|
|
268
|
+
if (options.codexSource) return thread.source === options.codexSource;
|
|
269
|
+
return ["cli", "vscode"].includes(thread.source);
|
|
263
270
|
});
|
|
271
|
+
if (!threads.length && options.codexSource && options.codexSource !== "cli") {
|
|
272
|
+
return { provider, discovered: 0, candidates: 0, imported: 0, skipped: 0, errors: [], details: {} };
|
|
273
|
+
}
|
|
264
274
|
if (!threads.length) return importJsonlProvider(provider, codexRoots(env), since, options, env);
|
|
265
275
|
|
|
266
276
|
const state = loadImportState(env);
|
|
@@ -288,7 +298,7 @@ function importCodexProvider(provider, since, options = {}, env = process.env) {
|
|
|
288
298
|
reportProgress(options, summary, index + 1, thread.rolloutPath);
|
|
289
299
|
continue;
|
|
290
300
|
}
|
|
291
|
-
const sourceType = thread
|
|
301
|
+
const sourceType = codexThreadSourceType(thread);
|
|
292
302
|
const fingerprint = `${fingerprintPrefix(sourceType)}:${fileFingerprint(thread.rolloutPath, stat)}:${codexSupplementFingerprint(thread)}`;
|
|
293
303
|
if (alreadyImported(state, thread.id, fingerprint, archived, provider)) {
|
|
294
304
|
summary.skipped++;
|
|
@@ -579,10 +589,12 @@ function structuredSessionUsesSharedRawFiles(provider, sourceType) {
|
|
|
579
589
|
|
|
580
590
|
function parseAgentJsonl(file, provider) {
|
|
581
591
|
const text = readTextMaybeZstd(file);
|
|
592
|
+
const events = [];
|
|
582
593
|
const messages = [];
|
|
583
594
|
let cwd = "";
|
|
584
595
|
let sessionId = "";
|
|
585
596
|
let title = "";
|
|
597
|
+
let titleSource = "";
|
|
586
598
|
const context = { model: "", tokenUsage: { input: 0, output: 0 } };
|
|
587
599
|
for (const line of text.split(/\r?\n/)) {
|
|
588
600
|
if (!line.trim()) continue;
|
|
@@ -592,6 +604,7 @@ function parseAgentJsonl(file, provider) {
|
|
|
592
604
|
} catch {
|
|
593
605
|
continue;
|
|
594
606
|
}
|
|
607
|
+
events.push(event);
|
|
595
608
|
cwd ||= firstString(event.cwd, event.working_directory, event.workingDirectory, event.context?.cwd, event.payload?.cwd);
|
|
596
609
|
sessionId ||= firstString(
|
|
597
610
|
event.session_id,
|
|
@@ -603,7 +616,20 @@ function parseAgentJsonl(file, provider) {
|
|
|
603
616
|
event.payload?.session_id,
|
|
604
617
|
event.sessionId
|
|
605
618
|
);
|
|
606
|
-
|
|
619
|
+
if (!title) {
|
|
620
|
+
const sourceTitle = firstString(event.title, event.conversation_title, event.payload?.title);
|
|
621
|
+
const aiTitle = firstString(event.aiTitle, event.ai_title);
|
|
622
|
+
if (sourceTitle) {
|
|
623
|
+
title = sourceTitle;
|
|
624
|
+
titleSource = "source";
|
|
625
|
+
} else if (aiTitle) {
|
|
626
|
+
title = aiTitle;
|
|
627
|
+
titleSource = "ai-title";
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
collectClaudeJsonlSessionMetadata(event, provider, context);
|
|
631
|
+
}
|
|
632
|
+
for (const event of events) {
|
|
607
633
|
updateCodexParseContext(event, provider, context);
|
|
608
634
|
updateClaudeParseContext(event, provider, context);
|
|
609
635
|
if (applyCodexTokenCount(event, provider, messages, context)) continue;
|
|
@@ -617,13 +643,292 @@ function parseAgentJsonl(file, provider) {
|
|
|
617
643
|
cwd,
|
|
618
644
|
sessionId,
|
|
619
645
|
title: title || inferredTitle,
|
|
620
|
-
titleSource:
|
|
646
|
+
titleSource: titleSource || (inferredTitle ? "first-user-prompt" : ""),
|
|
647
|
+
sessionSummary: claudeJsonlSessionSummary(provider, context),
|
|
621
648
|
messages: deduped,
|
|
622
649
|
startedAt: deduped[0]?.timestamp || "",
|
|
623
650
|
endedAt: deduped[deduped.length - 1]?.timestamp || ""
|
|
624
651
|
};
|
|
625
652
|
}
|
|
626
653
|
|
|
654
|
+
function collectClaudeJsonlSessionMetadata(event, provider, context = {}) {
|
|
655
|
+
if (!isClaudeJsonlProvider(provider) || !event || typeof event !== "object") return;
|
|
656
|
+
const metadata = context.claudeJsonl || (context.claudeJsonl = {
|
|
657
|
+
eventTypeCounts: {},
|
|
658
|
+
entrypoints: new Set(),
|
|
659
|
+
userTypes: new Set(),
|
|
660
|
+
versions: new Set(),
|
|
661
|
+
gitBranches: new Set(),
|
|
662
|
+
permissionModes: new Set(),
|
|
663
|
+
promptIds: new Set(),
|
|
664
|
+
requestIds: new Set(),
|
|
665
|
+
sourceToolAssistantUUIDs: new Set(),
|
|
666
|
+
attachmentTypes: new Set(),
|
|
667
|
+
remoteToolNames: new Set(),
|
|
668
|
+
mcpServerNames: new Set(),
|
|
669
|
+
toolNames: new Set(),
|
|
670
|
+
agentIds: new Set(),
|
|
671
|
+
slugs: new Set(),
|
|
672
|
+
sourceToolUseIDs: new Set(),
|
|
673
|
+
parentToolUseIDs: new Set(),
|
|
674
|
+
toolUseIDs: new Set(),
|
|
675
|
+
attributionSkills: new Set(),
|
|
676
|
+
queue: {},
|
|
677
|
+
attachmentDetails: {},
|
|
678
|
+
apiErrors: {},
|
|
679
|
+
mcpStructuredContentCount: 0,
|
|
680
|
+
aiTitles: new Set(),
|
|
681
|
+
lastPrompt: "",
|
|
682
|
+
sidechainMessageCount: 0,
|
|
683
|
+
messageEventCount: 0,
|
|
684
|
+
remoteControl: false
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
const type = firstString(event.type, event.kind);
|
|
688
|
+
if (type) metadata.eventTypeCounts[type] = (metadata.eventTypeCounts[type] || 0) + 1;
|
|
689
|
+
addSetValue(metadata.entrypoints, event.entrypoint);
|
|
690
|
+
addSetValue(metadata.userTypes, event.userType, event.user_type);
|
|
691
|
+
addSetValue(metadata.versions, event.version);
|
|
692
|
+
addSetValue(metadata.gitBranches, event.gitBranch, event.git_branch);
|
|
693
|
+
addSetValue(metadata.permissionModes, event.permissionMode, event.permission_mode);
|
|
694
|
+
addSetValue(metadata.promptIds, event.promptId, event.prompt_id);
|
|
695
|
+
addSetValue(metadata.requestIds, event.requestId, event.request_id);
|
|
696
|
+
addSetValue(metadata.sourceToolAssistantUUIDs, event.sourceToolAssistantUUID, event.source_tool_assistant_uuid);
|
|
697
|
+
addSetValue(metadata.agentIds, event.agentId, event.agent_id);
|
|
698
|
+
addSetValue(metadata.slugs, event.slug);
|
|
699
|
+
addSetValue(metadata.sourceToolUseIDs, event.sourceToolUseID, event.source_tool_use_id);
|
|
700
|
+
addSetValue(metadata.parentToolUseIDs, event.parentToolUseID, event.parent_tool_use_id);
|
|
701
|
+
addSetValue(metadata.toolUseIDs, event.toolUseID, event.tool_use_id);
|
|
702
|
+
addSetValue(metadata.attributionSkills, event.attributionSkill, event.attribution_skill);
|
|
703
|
+
if (event.mcpMeta?.structuredContent && typeof event.mcpMeta.structuredContent === "object") metadata.mcpStructuredContentCount++;
|
|
704
|
+
collectClaudeApiError(event, metadata);
|
|
705
|
+
if (event.isSidechain === true) metadata.sidechainMessageCount++;
|
|
706
|
+
if ((event.type === "user" || event.type === "assistant") && event.message) metadata.messageEventCount++;
|
|
707
|
+
|
|
708
|
+
const aiTitle = firstString(event.aiTitle, event.ai_title);
|
|
709
|
+
if (aiTitle) metadata.aiTitles.add(aiTitle);
|
|
710
|
+
const lastPrompt = firstString(event.lastPrompt, event.last_prompt);
|
|
711
|
+
if (lastPrompt) metadata.lastPrompt = lastPrompt;
|
|
712
|
+
|
|
713
|
+
if (event.type === "queue-operation") collectClaudeQueueOperation(event, metadata);
|
|
714
|
+
if (event.type === "attachment") collectClaudeAttachmentMetadata(event, metadata);
|
|
715
|
+
collectClaudeToolNames(event, metadata);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function collectClaudeQueueOperation(event, metadata) {
|
|
719
|
+
const op = firstString(event.operation).toLowerCase();
|
|
720
|
+
if (!op) return;
|
|
721
|
+
const key = op.replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "unknown";
|
|
722
|
+
metadata.queue[key] = (metadata.queue[key] || 0) + 1;
|
|
723
|
+
const timestamp = toIso(event.timestamp || event.created_at || event.createdAt || event.time);
|
|
724
|
+
if (timestamp) {
|
|
725
|
+
if (!metadata.queue.firstAt || timestamp < metadata.queue.firstAt) metadata.queue.firstAt = timestamp;
|
|
726
|
+
if (!metadata.queue.lastAt || timestamp > metadata.queue.lastAt) metadata.queue.lastAt = timestamp;
|
|
727
|
+
const upper = key.charAt(0).toUpperCase() + key.slice(1);
|
|
728
|
+
if (!metadata.queue[`first${upper}At`] || timestamp < metadata.queue[`first${upper}At`]) metadata.queue[`first${upper}At`] = timestamp;
|
|
729
|
+
if (!metadata.queue[`last${upper}At`] || timestamp > metadata.queue[`last${upper}At`]) metadata.queue[`last${upper}At`] = timestamp;
|
|
730
|
+
}
|
|
731
|
+
if (typeof event.content === "string" && event.content.trim()) {
|
|
732
|
+
metadata.queue.lastContent = event.content.trim();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function collectClaudeApiError(event, metadata) {
|
|
737
|
+
if (!event || typeof event !== "object") return;
|
|
738
|
+
if (event.isApiErrorMessage !== true && !event.error && event.apiErrorStatus == null) return;
|
|
739
|
+
const status = Number(event.apiErrorStatus);
|
|
740
|
+
if (Number.isFinite(status)) {
|
|
741
|
+
const key = String(status);
|
|
742
|
+
metadata.apiErrors.statusCounts = metadata.apiErrors.statusCounts || {};
|
|
743
|
+
metadata.apiErrors.statusCounts[key] = (metadata.apiErrors.statusCounts[key] || 0) + 1;
|
|
744
|
+
}
|
|
745
|
+
const type = firstString(event.error);
|
|
746
|
+
if (type) {
|
|
747
|
+
metadata.apiErrors.typeCounts = metadata.apiErrors.typeCounts || {};
|
|
748
|
+
metadata.apiErrors.typeCounts[type] = (metadata.apiErrors.typeCounts[type] || 0) + 1;
|
|
749
|
+
}
|
|
750
|
+
metadata.apiErrors.count = (metadata.apiErrors.count || 0) + 1;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function collectClaudeAttachmentMetadata(event, metadata) {
|
|
754
|
+
const attachment = event.attachment && typeof event.attachment === "object" ? event.attachment : {};
|
|
755
|
+
addSetValue(metadata.attachmentTypes, attachment.type);
|
|
756
|
+
collectClaudeAttachmentDetails(attachment, metadata);
|
|
757
|
+
for (const name of [...asStringArray(attachment.addedNames), ...asStringArray(attachment.removedNames)]) {
|
|
758
|
+
if (typeof name !== "string" || !name.trim()) continue;
|
|
759
|
+
const value = name.trim();
|
|
760
|
+
metadata.remoteToolNames.add(value);
|
|
761
|
+
if (isClaudeRemoteControlToolName(value)) metadata.remoteControl = true;
|
|
762
|
+
const server = claudeMcpServerNameFromTool(value);
|
|
763
|
+
if (server) metadata.mcpServerNames.add(server);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function collectClaudeAttachmentDetails(attachment, metadata) {
|
|
768
|
+
const type = firstString(attachment.type);
|
|
769
|
+
if (!type) return;
|
|
770
|
+
const details = metadata.attachmentDetails;
|
|
771
|
+
details.countByType = details.countByType || {};
|
|
772
|
+
details.countByType[type] = (details.countByType[type] || 0) + 1;
|
|
773
|
+
if (type === "nested_memory") {
|
|
774
|
+
details.nestedMemoryPaths = details.nestedMemoryPaths || new Set();
|
|
775
|
+
addSetValue(details.nestedMemoryPaths, firstString(attachment.path, attachment.content?.path, attachment.displayPath));
|
|
776
|
+
} else if (type === "edited_text_file") {
|
|
777
|
+
details.editedTextFiles = details.editedTextFiles || new Set();
|
|
778
|
+
addSetValue(details.editedTextFiles, firstString(attachment.path, attachment.content?.path, attachment.displayPath));
|
|
779
|
+
} else if (type === "queued_command") {
|
|
780
|
+
details.queuedCommandCount = (details.queuedCommandCount || 0) + 1;
|
|
781
|
+
details.lastQueuedCommand = compactMetadata({
|
|
782
|
+
prompt: firstString(attachment.prompt),
|
|
783
|
+
commandMode: firstString(attachment.commandMode, attachment.command_mode)
|
|
784
|
+
});
|
|
785
|
+
} else if (type === "command_permissions") {
|
|
786
|
+
details.commandPermissionCount = (details.commandPermissionCount || 0) + 1;
|
|
787
|
+
details.lastAllowedTools = asStringArray(attachment.allowedTools);
|
|
788
|
+
} else if (type === "date_change") {
|
|
789
|
+
details.dateChanges = details.dateChanges || new Set();
|
|
790
|
+
addSetValue(details.dateChanges, attachment.newDate, attachment.new_date);
|
|
791
|
+
} else if (type === "max_turns_reached") {
|
|
792
|
+
details.maxTurnsReachedCount = (details.maxTurnsReachedCount || 0) + 1;
|
|
793
|
+
details.lastMaxTurnsReached = compactMetadata({
|
|
794
|
+
maxTurns: numericValue(attachment.maxTurns, attachment.max_turns),
|
|
795
|
+
turnCount: numericValue(attachment.turnCount, attachment.turn_count)
|
|
796
|
+
});
|
|
797
|
+
} else if (type === "compact_file_reference") {
|
|
798
|
+
details.compactFileReferences = details.compactFileReferences || new Set();
|
|
799
|
+
addSetValue(details.compactFileReferences, attachment.path, attachment.displayPath);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function collectClaudeToolNames(event, metadata) {
|
|
804
|
+
const parts = Array.isArray(event.message?.content) ? event.message.content : [];
|
|
805
|
+
for (const part of parts) {
|
|
806
|
+
if (!part || typeof part !== "object") continue;
|
|
807
|
+
const type = String(part.type || part.kind || "").toLowerCase();
|
|
808
|
+
if (type !== "tool_use" && type !== "server_tool_use" && type !== "mcp_tool_use" && type !== "function_call") continue;
|
|
809
|
+
addSetValue(metadata.toolNames, part.name, part.tool_name, part.toolName, part.function?.name);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function claudeMcpServerNameFromTool(name) {
|
|
814
|
+
const match = String(name || "").match(/^mcp__(.+?)__/);
|
|
815
|
+
if (!match) return "";
|
|
816
|
+
return match[1];
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const CLAUDE_REMOTE_CONTROL_TOOL_NAMES = new Set([
|
|
820
|
+
"RemoteTrigger",
|
|
821
|
+
"TaskOutput",
|
|
822
|
+
"TaskStop",
|
|
823
|
+
"PushNotification",
|
|
824
|
+
"AskUserQuestion",
|
|
825
|
+
"Monitor",
|
|
826
|
+
"TaskCreate",
|
|
827
|
+
"TaskUpdate",
|
|
828
|
+
"TaskList",
|
|
829
|
+
"TaskGet",
|
|
830
|
+
"CronCreate",
|
|
831
|
+
"CronDelete",
|
|
832
|
+
"CronList"
|
|
833
|
+
]);
|
|
834
|
+
|
|
835
|
+
function isClaudeRemoteControlToolName(name) {
|
|
836
|
+
const value = String(name || "").trim();
|
|
837
|
+
return CLAUDE_REMOTE_CONTROL_TOOL_NAMES.has(value);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function claudeJsonlSessionSummary(provider, context = {}) {
|
|
841
|
+
if (!isClaudeJsonlProvider(provider)) return null;
|
|
842
|
+
const metadata = context.claudeJsonl;
|
|
843
|
+
if (!metadata) return null;
|
|
844
|
+
const remoteToolNames = sortedSet(metadata.remoteToolNames);
|
|
845
|
+
const mcpServerNames = sortedSet(metadata.mcpServerNames);
|
|
846
|
+
const attachmentDetails = claudeAttachmentDetailsSummary(metadata.attachmentDetails);
|
|
847
|
+
const result = compactMetadata({
|
|
848
|
+
claudeJsonl: compactMetadata({
|
|
849
|
+
entrypoint: singleOrArray(metadata.entrypoints),
|
|
850
|
+
userType: singleOrArray(metadata.userTypes),
|
|
851
|
+
version: singleOrArray(metadata.versions),
|
|
852
|
+
gitBranch: singleOrArray(metadata.gitBranches),
|
|
853
|
+
permissionModes: sortedSet(metadata.permissionModes),
|
|
854
|
+
eventTypeCounts: compactMetadata(metadata.eventTypeCounts),
|
|
855
|
+
messageEventCount: metadata.messageEventCount || undefined,
|
|
856
|
+
sidechainMessageCount: metadata.sidechainMessageCount || undefined,
|
|
857
|
+
promptIdCount: metadata.promptIds.size || undefined,
|
|
858
|
+
requestIdCount: metadata.requestIds.size || undefined,
|
|
859
|
+
sourceToolAssistantUUIDCount: metadata.sourceToolAssistantUUIDs.size || undefined,
|
|
860
|
+
agentIds: sortedSet(metadata.agentIds),
|
|
861
|
+
slugs: sortedSet(metadata.slugs),
|
|
862
|
+
sourceToolUseIDCount: metadata.sourceToolUseIDs.size || undefined,
|
|
863
|
+
parentToolUseIDCount: metadata.parentToolUseIDs.size || undefined,
|
|
864
|
+
toolUseIDCount: metadata.toolUseIDs.size || undefined,
|
|
865
|
+
attributionSkills: sortedSet(metadata.attributionSkills),
|
|
866
|
+
mcpStructuredContentCount: metadata.mcpStructuredContentCount || undefined,
|
|
867
|
+
apiErrors: compactMetadata(metadata.apiErrors),
|
|
868
|
+
attachmentTypes: sortedSet(metadata.attachmentTypes),
|
|
869
|
+
attachmentDetails,
|
|
870
|
+
toolNames: sortedSet(metadata.toolNames),
|
|
871
|
+
aiTitle: sortedSet(metadata.aiTitles)[0] || undefined,
|
|
872
|
+
lastPrompt: metadata.lastPrompt || undefined,
|
|
873
|
+
queue: compactMetadata(metadata.queue),
|
|
874
|
+
remoteControl: metadata.remoteControl ? compactMetadata({
|
|
875
|
+
detected: true,
|
|
876
|
+
toolCount: remoteToolNames.length || undefined,
|
|
877
|
+
toolNames: remoteToolNames,
|
|
878
|
+
mcpToolCount: remoteToolNames.filter((name) => name.startsWith("mcp__")).length || undefined,
|
|
879
|
+
mcpServerNames
|
|
880
|
+
}) : undefined
|
|
881
|
+
})
|
|
882
|
+
});
|
|
883
|
+
return result || null;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function claudeAttachmentDetailsSummary(details = {}) {
|
|
887
|
+
return compactMetadata({
|
|
888
|
+
countByType: compactMetadata(details.countByType),
|
|
889
|
+
nestedMemoryPaths: details.nestedMemoryPaths ? sortedSet(details.nestedMemoryPaths) : undefined,
|
|
890
|
+
editedTextFiles: details.editedTextFiles ? sortedSet(details.editedTextFiles) : undefined,
|
|
891
|
+
queuedCommandCount: details.queuedCommandCount || undefined,
|
|
892
|
+
lastQueuedCommand: details.lastQueuedCommand || undefined,
|
|
893
|
+
commandPermissionCount: details.commandPermissionCount || undefined,
|
|
894
|
+
lastAllowedTools: details.lastAllowedTools?.length ? details.lastAllowedTools : undefined,
|
|
895
|
+
dateChanges: details.dateChanges ? sortedSet(details.dateChanges) : undefined,
|
|
896
|
+
maxTurnsReachedCount: details.maxTurnsReachedCount || undefined,
|
|
897
|
+
lastMaxTurnsReached: details.lastMaxTurnsReached || undefined,
|
|
898
|
+
compactFileReferences: details.compactFileReferences ? sortedSet(details.compactFileReferences) : undefined
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function addSetValue(set, ...values) {
|
|
903
|
+
for (const value of values) {
|
|
904
|
+
if (typeof value === "string" && value.trim()) set.add(value.trim());
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function asStringArray(value) {
|
|
909
|
+
if (!Array.isArray(value)) return [];
|
|
910
|
+
return value.filter((item) => typeof item === "string" && item.trim()).map((item) => item.trim());
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function numericValue(...values) {
|
|
914
|
+
for (const value of values) {
|
|
915
|
+
if (value == null || value === "") continue;
|
|
916
|
+
const number = Number(value);
|
|
917
|
+
if (Number.isFinite(number)) return number;
|
|
918
|
+
}
|
|
919
|
+
return undefined;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function sortedSet(set) {
|
|
923
|
+
return [...set].sort((a, b) => a.localeCompare(b));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function singleOrArray(set) {
|
|
927
|
+
const values = sortedSet(set);
|
|
928
|
+
if (!values.length) return undefined;
|
|
929
|
+
return values.length === 1 ? values[0] : values;
|
|
930
|
+
}
|
|
931
|
+
|
|
627
932
|
function repoInfoForImport(provider, cwd, metadata = null) {
|
|
628
933
|
const repoCwd = repoCwdForImport(provider, cwd, metadata);
|
|
629
934
|
if (!repoCwd) return null;
|
|
@@ -665,7 +970,6 @@ function isClaudeJsonlProvider(provider) {
|
|
|
665
970
|
}
|
|
666
971
|
|
|
667
972
|
function jsonlSessionTitleForImport(parsed, metadata = null) {
|
|
668
|
-
if (parsed?.titleSource === "source") return parsed.title || "";
|
|
669
973
|
return firstString(metadata?.title, parsed?.title);
|
|
670
974
|
}
|
|
671
975
|
|
|
@@ -701,6 +1005,23 @@ function claudeCodeSidecarSessionSummary(metadata = null) {
|
|
|
701
1005
|
});
|
|
702
1006
|
}
|
|
703
1007
|
|
|
1008
|
+
function mergeSessionSummaries(...summaries) {
|
|
1009
|
+
const result = {};
|
|
1010
|
+
for (const summary of summaries) {
|
|
1011
|
+
if (!summary || typeof summary !== "object" || Array.isArray(summary)) continue;
|
|
1012
|
+
for (const [key, value] of Object.entries(summary)) {
|
|
1013
|
+
if (key === "modelUsage" && Array.isArray(value)) {
|
|
1014
|
+
result.modelUsage = [...(Array.isArray(result.modelUsage) ? result.modelUsage : []), ...value];
|
|
1015
|
+
} else if (value && typeof value === "object" && !Array.isArray(value) && result[key] && typeof result[key] === "object" && !Array.isArray(result[key])) {
|
|
1016
|
+
result[key] = compactMetadata({ ...result[key], ...value });
|
|
1017
|
+
} else {
|
|
1018
|
+
result[key] = value;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return compactMetadata(result);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
704
1025
|
function firstExistingDirectory(...values) {
|
|
705
1026
|
for (const value of values) {
|
|
706
1027
|
if (typeof value !== "string" || !value.trim()) continue;
|
|
@@ -757,9 +1078,54 @@ function dedupeAdjacentMessages(messages) {
|
|
|
757
1078
|
}
|
|
758
1079
|
result.push(message);
|
|
759
1080
|
}
|
|
1081
|
+
return dedupeDuplicateToolResults(result);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
function dedupeDuplicateToolResults(messages) {
|
|
1085
|
+
const result = [];
|
|
1086
|
+
const toolResultsByKey = new Map();
|
|
1087
|
+
for (const message of messages) {
|
|
1088
|
+
const key = duplicateToolResultKey(message);
|
|
1089
|
+
if (key && toolResultsByKey.has(key)) {
|
|
1090
|
+
const existingIndex = toolResultsByKey.get(key);
|
|
1091
|
+
const existing = result[existingIndex];
|
|
1092
|
+
if (timestampsNear(existing?.timestamp, message?.timestamp)) {
|
|
1093
|
+
result[existingIndex] = preferredToolResultMessage(existing, message);
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (key) toolResultsByKey.set(key, result.length);
|
|
1098
|
+
result.push(message);
|
|
1099
|
+
}
|
|
760
1100
|
return result;
|
|
761
1101
|
}
|
|
762
1102
|
|
|
1103
|
+
function duplicateToolResultKey(message) {
|
|
1104
|
+
const result = message?.metadata?.toolResult;
|
|
1105
|
+
const id = firstString(result?.id, result?.callId, result?.call_id, result?.toolCallId, result?.tool_call_id, result?.toolUseId, result?.tool_use_id);
|
|
1106
|
+
if (!id || message?.role !== "tool") return "";
|
|
1107
|
+
return `${message?.metadata?.provider || result?.provider || ""}:${id}`;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function preferredToolResultMessage(left, right) {
|
|
1111
|
+
return toolResultMessageDisplayScore(right) > toolResultMessageDisplayScore(left) ? right : left;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function toolResultMessageDisplayScore(message) {
|
|
1115
|
+
const result = message?.metadata?.toolResult || {};
|
|
1116
|
+
const rawCategory = normalizeToolToken(result.rawCategory);
|
|
1117
|
+
const kind = normalizeToolToken(result.kind);
|
|
1118
|
+
const category = normalizeToolToken(result.category);
|
|
1119
|
+
let score = 0;
|
|
1120
|
+
if (["exec_command_end", "patch_apply_end", "mcp_tool_call_end", "web_search_end", "tool_result", "tool_output"].includes(rawCategory)) score += 2000;
|
|
1121
|
+
if (rawCategory === "function_call_output" || rawCategory === "custom_tool_call_output") score -= 1000;
|
|
1122
|
+
if (category && category !== "function") score += 250;
|
|
1123
|
+
if (kind && !kind.startsWith("call_")) score += 150;
|
|
1124
|
+
if (/^\$\s/.test(String(result.summary || result.output || message?.content || ""))) score += 250;
|
|
1125
|
+
score += Math.min(200, String(result.output || message?.content || "").length / 200);
|
|
1126
|
+
return score;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
763
1129
|
function hasStructuredTranscriptMetadata(message) {
|
|
764
1130
|
const metadata = message?.metadata || {};
|
|
765
1131
|
return Boolean(
|
|
@@ -2487,6 +2853,7 @@ function discoverCliHistory(env = process.env, options = {}) {
|
|
|
2487
2853
|
const codexThreads = readCodexSessionEntries(env);
|
|
2488
2854
|
publish("codexCli", "Codex CLI", summarizeCodexThreads(codexThreads, "cli"));
|
|
2489
2855
|
publish("codexDesktop", "Codex Desktop", summarizeCodexThreads(codexThreads, "vscode"));
|
|
2856
|
+
publish("codexSdk", "Codex SDK jobs", summarizeCodexThreads(codexThreads, "exec"));
|
|
2490
2857
|
result.codex = summarizeCodexThreads(codexThreads);
|
|
2491
2858
|
|
|
2492
2859
|
const claudeScan = scanClaudeProjectFiles({
|
|
@@ -2613,8 +2980,8 @@ function summarizeCodex(env = process.env, source) {
|
|
|
2613
2980
|
|
|
2614
2981
|
function summarizeCodexThreads(allThreads, source) {
|
|
2615
2982
|
const threads = allThreads.filter((thread) => {
|
|
2616
|
-
if (
|
|
2617
|
-
return
|
|
2983
|
+
if (source) return thread.source === source;
|
|
2984
|
+
return ["cli", "vscode"].includes(thread.source);
|
|
2618
2985
|
});
|
|
2619
2986
|
if (threads.length) {
|
|
2620
2987
|
const oldest = threads.length
|
|
@@ -2629,10 +2996,24 @@ function summarizeCodexThreads(allThreads, source) {
|
|
|
2629
2996
|
? "terminal `codex` sessions"
|
|
2630
2997
|
: source === "vscode"
|
|
2631
2998
|
? "Codex desktop app sessions"
|
|
2999
|
+
: source === "exec"
|
|
3000
|
+
? "Codex exec/SDK batch jobs; opt-in"
|
|
2632
3001
|
: "includes Codex CLI and Codex Desktop top-level sessions"
|
|
2633
3002
|
};
|
|
2634
3003
|
}
|
|
2635
|
-
return {
|
|
3004
|
+
return {
|
|
3005
|
+
sessions: 0,
|
|
3006
|
+
oldest: "",
|
|
3007
|
+
details: {},
|
|
3008
|
+
note:
|
|
3009
|
+
source === "cli"
|
|
3010
|
+
? "terminal `codex` sessions"
|
|
3011
|
+
: source === "vscode"
|
|
3012
|
+
? "Codex desktop app sessions"
|
|
3013
|
+
: source === "exec"
|
|
3014
|
+
? "Codex exec/SDK batch jobs; opt-in"
|
|
3015
|
+
: ""
|
|
3016
|
+
};
|
|
2636
3017
|
}
|
|
2637
3018
|
|
|
2638
3019
|
function summarizeClaude() {
|
|
@@ -2888,11 +3269,13 @@ function classifyClaudeFile(file) {
|
|
|
2888
3269
|
}
|
|
2889
3270
|
let inspected = 0;
|
|
2890
3271
|
let sawSdkMessage = false;
|
|
3272
|
+
let sawRemoteControlTools = false;
|
|
2891
3273
|
for (const line of lines) {
|
|
2892
3274
|
if (!line.trim()) continue;
|
|
2893
3275
|
inspected++;
|
|
2894
3276
|
try {
|
|
2895
3277
|
const event = JSON.parse(line);
|
|
3278
|
+
if (isClaudeRemoteControlToolDelta(event)) sawRemoteControlTools = true;
|
|
2896
3279
|
if ((event.type === "user" || event.type === "assistant") && event.message) {
|
|
2897
3280
|
if (event.entrypoint === "sdk-cli") sawSdkMessage = true;
|
|
2898
3281
|
else return "conversation";
|
|
@@ -2900,9 +3283,14 @@ function classifyClaudeFile(file) {
|
|
|
2900
3283
|
} catch {
|
|
2901
3284
|
return "other";
|
|
2902
3285
|
}
|
|
2903
|
-
if (inspected >= 80) return sawSdkMessage ? "sdk-job" : "other";
|
|
3286
|
+
if (inspected >= 80) return sawSdkMessage ? (sawRemoteControlTools ? "conversation" : "sdk-job") : "other";
|
|
2904
3287
|
}
|
|
2905
|
-
return sawSdkMessage ? "sdk-job" : "other";
|
|
3288
|
+
return sawSdkMessage ? (sawRemoteControlTools ? "conversation" : "sdk-job") : "other";
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
function isClaudeRemoteControlToolDelta(event) {
|
|
3292
|
+
if (!event || event.type !== "attachment" || !Array.isArray(event.attachment?.addedNames)) return false;
|
|
3293
|
+
return event.attachment.addedNames.some((name) => isClaudeRemoteControlToolName(name));
|
|
2906
3294
|
}
|
|
2907
3295
|
|
|
2908
3296
|
function readInitialLines(file, maxLines, maxBytes = 1024 * 1024) {
|
|
@@ -3173,9 +3561,10 @@ function codexSupplementFingerprint(thread) {
|
|
|
3173
3561
|
function summarizeCodexSources(threads) {
|
|
3174
3562
|
const cli = threads.filter((thread) => thread.source === "cli").length;
|
|
3175
3563
|
const desktop = threads.filter((thread) => thread.source === "vscode").length;
|
|
3564
|
+
const exec = threads.filter((thread) => thread.source === "exec").length;
|
|
3176
3565
|
const summaries = threads.filter((thread) => thread.rawMemory || thread.rolloutSummary).length;
|
|
3177
3566
|
const archived = threads.filter((thread) => thread.sourceDetail === "archived_sessions").length;
|
|
3178
|
-
return { cli, desktop, ...(archived ? { archived } : {}), ...(summaries ? { summaries } : {}) };
|
|
3567
|
+
return { cli, desktop, ...(exec ? { exec } : {}), ...(archived ? { archived } : {}), ...(summaries ? { summaries } : {}) };
|
|
3179
3568
|
}
|
|
3180
3569
|
|
|
3181
3570
|
function readClaudeDesktopSessions(options = {}, env = process.env) {
|
package/src/parser-versions.js
CHANGED
package/src/search.js
CHANGED
|
@@ -1103,6 +1103,7 @@ function normalizeProviderFilter(value) {
|
|
|
1103
1103
|
codex: { provider: "codex" },
|
|
1104
1104
|
codex_cli: { provider: "codex", sourceType: "codex-cli-history", sourceTypes: ["codex-cli-history", "cli-history"] },
|
|
1105
1105
|
codex_desktop: { provider: "codex", sourceType: "codex-desktop-history", sourceTypes: ["codex-desktop-history"] },
|
|
1106
|
+
codex_sdk: { provider: "codex", sourceType: "codex-sdk-history", sourceTypes: ["codex-sdk-history"] },
|
|
1106
1107
|
cursor: { provider: "cursor" },
|
|
1107
1108
|
cline: { provider: "cline", sourceType: "cline-task-history", sourceTypes: ["cline-task-history"] },
|
|
1108
1109
|
opencode: { provider: "opencode", sourceTypes: ["opencode-cli-history", "opencode-cli-sqlite-history", "opencode-desktop-history", "opencode-desktop-sqlite-history", "opencode-web-sqlite-history", "opencode-history", "opencode-sqlite-history"] },
|
package/src/sources.js
CHANGED
|
@@ -6,6 +6,7 @@ const SOURCE_GROUPS = [
|
|
|
6
6
|
sources: [
|
|
7
7
|
{ source: "codex-cli", provider: "codex", sourceType: "codex-cli-history", label: "Codex CLI" },
|
|
8
8
|
{ source: "codex-desktop", provider: "codex", sourceType: "codex-desktop-history", label: "Codex Desktop" },
|
|
9
|
+
{ source: "codex-sdk", provider: "codex", sourceType: "codex-sdk-history", label: "Codex SDK jobs" },
|
|
9
10
|
{ source: "chatgpt", provider: "chatgpt", label: "ChatGPT" }
|
|
10
11
|
]
|
|
11
12
|
},
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const WEB_EXPORT_INSTRUCTIONS = {
|
|
4
|
+
chatgpt: {
|
|
5
|
+
source: "chatgpt",
|
|
6
|
+
provider: "chatgpt",
|
|
7
|
+
label: "ChatGPT",
|
|
8
|
+
requestUrl: "https://privacy.openai.com/policies/en/",
|
|
9
|
+
helpUrl: "https://help.openai.com/en/articles/7260999-how-do-i-export-my-chatgpt-history-and-data",
|
|
10
|
+
fileDescription: "official ChatGPT data export ZIP or extracted folder",
|
|
11
|
+
importCommand: "agentlog import chatgpt <path-to-export.zip> --username <email> --scope local",
|
|
12
|
+
steps: [
|
|
13
|
+
"Open the OpenAI Privacy Portal, choose Make a Privacy Request, select I have a consumer ChatGPT account, then select Download my data.",
|
|
14
|
+
"Or in ChatGPT, open your profile menu, choose Settings, open Data Controls, then use Export Data.",
|
|
15
|
+
"Complete account verification and wait for the export email.",
|
|
16
|
+
"Download the ZIP before the email link expires, then pass the ZIP or extracted folder to agentlog."
|
|
17
|
+
],
|
|
18
|
+
notes: [
|
|
19
|
+
"OpenAI says ChatGPT Business and Enterprise chat exports are not available through the ChatGPT account export flow.",
|
|
20
|
+
"OpenAI export emails can take time to arrive, and the download link expires after 24 hours."
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"claude-web": {
|
|
24
|
+
source: "claude-web",
|
|
25
|
+
provider: "claude_web",
|
|
26
|
+
label: "Claude.ai",
|
|
27
|
+
requestUrl: "https://claude.ai/settings/privacy",
|
|
28
|
+
helpUrl: "https://support.claude.com/en/articles/9450526-how-can-i-export-my-claude-data",
|
|
29
|
+
fileDescription: "official Claude.ai data export ZIP or extracted folder",
|
|
30
|
+
importCommand: "agentlog import claude-web <path-to-export.zip> --username <email-or-name> --scope local",
|
|
31
|
+
steps: [
|
|
32
|
+
"Open Claude on the web or Claude Desktop and go to Settings > Privacy.",
|
|
33
|
+
"Click Export data.",
|
|
34
|
+
"Wait for Claude to email the download link to the address on the account.",
|
|
35
|
+
"Download the ZIP before the email link expires, then pass the ZIP or extracted folder to agentlog."
|
|
36
|
+
],
|
|
37
|
+
notes: [
|
|
38
|
+
"Claude's mobile apps cannot start a data export.",
|
|
39
|
+
"Claude requires you to be signed in when using the emailed download link, and the link expires after 24 hours."
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function canonicalWebExportInstructionSource(source) {
|
|
45
|
+
const value = String(source || "").trim().toLowerCase().replace(/[_\s]+/g, "-");
|
|
46
|
+
if (value === "chatgpt" || value === "openai") return "chatgpt";
|
|
47
|
+
if (value === "claude-ai" || value === "claude-web") return "claude-web";
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function webExportInstructions(source) {
|
|
52
|
+
const key = canonicalWebExportInstructionSource(source);
|
|
53
|
+
return key ? WEB_EXPORT_INSTRUCTIONS[key] : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function manualImportInstructionResult(source) {
|
|
57
|
+
const instructions = webExportInstructions(source);
|
|
58
|
+
if (!instructions) throw new Error(`unknown web export instruction source: ${source}`);
|
|
59
|
+
return {
|
|
60
|
+
provider: instructions.provider,
|
|
61
|
+
source: instructions.source,
|
|
62
|
+
manual: true,
|
|
63
|
+
instructions,
|
|
64
|
+
discovered: 0,
|
|
65
|
+
candidates: 0,
|
|
66
|
+
imported: 0,
|
|
67
|
+
skipped: 0,
|
|
68
|
+
errors: []
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
WEB_EXPORT_INSTRUCTIONS,
|
|
74
|
+
canonicalWebExportInstructionSource,
|
|
75
|
+
manualImportInstructionResult,
|
|
76
|
+
webExportInstructions
|
|
77
|
+
};
|