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/cli.js
CHANGED
|
@@ -24,6 +24,7 @@ const { runSupervisorForeground, startSupervisorDetached, stopSupervisor, superv
|
|
|
24
24
|
const { configureRemoteFromFlags, hasRemoteTarget, listRemoteSnapshots, replaceRemoteArchive, snapshotArchive, syncArchive, wipeRemoteArchive } = require("./sync");
|
|
25
25
|
const { version } = require("./version");
|
|
26
26
|
const { listWebAccounts, renameWebAccount } = require("./web-accounts");
|
|
27
|
+
const { webExportInstructions } = require("./web-export-instructions");
|
|
27
28
|
|
|
28
29
|
const HISTORY_AUTH_COOKIE = "agentlog_history";
|
|
29
30
|
const SESSION_WEB_PAYLOAD_VERSION = 3;
|
|
@@ -2185,21 +2186,20 @@ async function importCommand(args, flags, env) {
|
|
|
2185
2186
|
printImportResults([result]);
|
|
2186
2187
|
return;
|
|
2187
2188
|
}
|
|
2188
|
-
if (sub === "claude-web" || sub === "chatgpt") {
|
|
2189
|
-
|
|
2190
|
-
if (
|
|
2191
|
-
|
|
2192
|
-
printMuted("Choose the official export JSON, ZIP, or extracted folder.");
|
|
2193
|
-
importFile = (await ask(" Export path: ")).trim();
|
|
2189
|
+
if (sub === "claude-web" || sub === "claude_web" || sub === "chatgpt") {
|
|
2190
|
+
const instructions = webExportInstructions(sub);
|
|
2191
|
+
if (flags.instructions || flags.instruction || flags.docs) {
|
|
2192
|
+
return printWebExportInstructions(instructions, flags);
|
|
2194
2193
|
}
|
|
2195
|
-
|
|
2194
|
+
let importFile = flags.file || args[1] || "";
|
|
2195
|
+
if (!importFile) return printWebExportInstructions(instructions, flags);
|
|
2196
2196
|
let username = flags.username || "";
|
|
2197
2197
|
let displayName = flags["display-name"] || flags.displayName || "";
|
|
2198
2198
|
if (!username && process.stdin.isTTY) {
|
|
2199
2199
|
username = (await ask(`${sub} account username/email: `)).trim();
|
|
2200
2200
|
if (!displayName) displayName = (await ask(`Display name [${username}]: `)).trim() || username;
|
|
2201
2201
|
}
|
|
2202
|
-
const result = importWebChat(
|
|
2202
|
+
const result = importWebChat(instructions.source, path.resolve(importFile), {
|
|
2203
2203
|
scope: flags.scope || "local",
|
|
2204
2204
|
dryRun: flags["dry-run"],
|
|
2205
2205
|
username,
|
|
@@ -2827,10 +2827,13 @@ function compareHistorySessionsForTree(a, b) {
|
|
|
2827
2827
|
|
|
2828
2828
|
function statsPayload(filters, env) {
|
|
2829
2829
|
const sessions = listHistorySessions({ ...filters, limit: 5000 }, env);
|
|
2830
|
-
return statsPayloadForSessions(sessions);
|
|
2830
|
+
return statsPayloadForSessions(sessions, { includeSdk: statsFiltersTargetSdk(filters) });
|
|
2831
2831
|
}
|
|
2832
2832
|
|
|
2833
2833
|
function statsPayloadForSessions(sessions, options = {}) {
|
|
2834
|
+
const allSessions = Array.isArray(sessions) ? sessions : [];
|
|
2835
|
+
const sdkSessions = allSessions.filter(isSdkStatsSession);
|
|
2836
|
+
const statsSessions = options.includeSdk ? allSessions : allSessions.filter((session) => !isSdkStatsSession(session));
|
|
2834
2837
|
const providerSet = new Set();
|
|
2835
2838
|
const companySet = new Set();
|
|
2836
2839
|
const modelGroupSet = new Set();
|
|
@@ -2853,7 +2856,7 @@ function statsPayloadForSessions(sessions, options = {}) {
|
|
|
2853
2856
|
let totalUserMessages = 0;
|
|
2854
2857
|
let peakSessionTokens = 0;
|
|
2855
2858
|
let peakSessionLabel = "";
|
|
2856
|
-
for (const session of
|
|
2859
|
+
for (const session of statsSessions) {
|
|
2857
2860
|
const provider = String(session.provider || "unknown");
|
|
2858
2861
|
const modelGroup = statsSessionPrimaryModel(session);
|
|
2859
2862
|
const companyGroup = statsSessionCompany(session, provider, modelGroup);
|
|
@@ -3114,15 +3117,25 @@ function statsPayloadForSessions(sessions, options = {}) {
|
|
|
3114
3117
|
const avgMessagesPerConversation =
|
|
3115
3118
|
totalConversations > 0 ? totalMessages / totalConversations : 0;
|
|
3116
3119
|
const splitStats = options.includeSplit === false ? null : {
|
|
3117
|
-
agent: statsPayloadForSessions(
|
|
3118
|
-
chat: statsPayloadForSessions(
|
|
3120
|
+
agent: statsPayloadForSessions(allSessions.filter((session) => !isSdkStatsSession(session) && statsSessionCategory(session) === "agent"), { includeSplit: false, category: "agent" }),
|
|
3121
|
+
chat: statsPayloadForSessions(allSessions.filter((session) => !isSdkStatsSession(session) && statsSessionCategory(session) === "chat"), { includeSplit: false, category: "chat" }),
|
|
3122
|
+
sdk: statsPayloadForSessions(sdkSessions, { includeSplit: false, includeSdk: true, category: "sdk" })
|
|
3119
3123
|
};
|
|
3124
|
+
const sdkStats = splitStats?.sdk || null;
|
|
3120
3125
|
return {
|
|
3121
3126
|
category: options.category || "all",
|
|
3122
3127
|
generated_at: new Date().toISOString(),
|
|
3123
3128
|
session_count: totalConversations,
|
|
3124
3129
|
agent_session_count: splitStats ? splitStats.agent.session_count : undefined,
|
|
3125
3130
|
chat_session_count: splitStats ? splitStats.chat.session_count : undefined,
|
|
3131
|
+
sdk_session_count: sdkStats ? sdkStats.session_count : undefined,
|
|
3132
|
+
sdk_message_count: sdkStats ? sdkStats.message_count : undefined,
|
|
3133
|
+
sdk_user_message_count: sdkStats ? sdkStats.user_message_count : undefined,
|
|
3134
|
+
sdk_total_tokens: sdkStats ? sdkStats.total_tokens : undefined,
|
|
3135
|
+
sdk_total_input_tokens: sdkStats ? sdkStats.total_input_tokens : undefined,
|
|
3136
|
+
sdk_total_output_tokens: sdkStats ? sdkStats.total_output_tokens : undefined,
|
|
3137
|
+
sdk_total_cache_tokens: sdkStats ? sdkStats.total_cache_tokens : undefined,
|
|
3138
|
+
sdk_total_estimated_tokens: sdkStats ? sdkStats.total_estimated_tokens : undefined,
|
|
3126
3139
|
message_count: totalMessages,
|
|
3127
3140
|
user_message_count: totalUserMessages,
|
|
3128
3141
|
total_tokens: totalTokens,
|
|
@@ -3221,6 +3234,20 @@ function statsSessionCategory(session) {
|
|
|
3221
3234
|
return provider === "chatgpt" || provider === "claude_web" ? "chat" : "agent";
|
|
3222
3235
|
}
|
|
3223
3236
|
|
|
3237
|
+
const SDK_STATS_SOURCE_TYPES = new Set(["codex-sdk-history", "claude-sdk-history"]);
|
|
3238
|
+
|
|
3239
|
+
function isSdkStatsSession(session) {
|
|
3240
|
+
const provider = String(session?.provider || "").toLowerCase();
|
|
3241
|
+
const sourceType = String(session?.sourceType || session?.source_type || "").toLowerCase();
|
|
3242
|
+
return provider === "claude_sdk" || SDK_STATS_SOURCE_TYPES.has(sourceType);
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
function statsFiltersTargetSdk(filters = {}) {
|
|
3246
|
+
const provider = String(filters.provider || filters.source || "").trim().toLowerCase().replace(/[-\s]+/g, "_");
|
|
3247
|
+
const sourceType = String(filters.sourceType || filters.source_type || "").trim().toLowerCase();
|
|
3248
|
+
return provider === "codex_sdk" || provider === "claude_sdk" || SDK_STATS_SOURCE_TYPES.has(sourceType);
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3224
3251
|
function isWebChatStatsProvider(provider) {
|
|
3225
3252
|
return provider === "chatgpt" || provider === "claude_web";
|
|
3226
3253
|
}
|
|
@@ -3513,7 +3540,7 @@ function serverCommand(flags, env) {
|
|
|
3513
3540
|
}
|
|
3514
3541
|
|
|
3515
3542
|
const RECALL_TARGET_SOURCE_MAP = {
|
|
3516
|
-
codex: ["codex-cli", "codex-desktop"],
|
|
3543
|
+
codex: ["codex-cli", "codex-desktop", "codex-sdk"],
|
|
3517
3544
|
claude: ["claude", "claude-code-desktop", "claude-workspace"],
|
|
3518
3545
|
gemini: ["gemini-cli"],
|
|
3519
3546
|
antigravity: ["antigravity"],
|
|
@@ -4037,7 +4064,7 @@ function recallArchiveHints() {
|
|
|
4037
4064
|
return `- Sessions live under \`~/.agentlog/data/agentlog/sessions/repo=<repo-or-path-key>/provider=<provider>/year=YYYY/month=MM/day=DD/session=<session_id>.conversation.md\`.
|
|
4038
4065
|
- Git repositories use canonical keys like \`github.com/org/repo\`. Non-git directories may use stable \`path:<hash>\` storage keys, but history results include \`repo_display\` and \`cwd\` with the readable local path.
|
|
4039
4066
|
- When the user names a repo or folder, add \`--repo "<repo-or-path>"\`; it matches canonical repo keys, \`path:<hash>\`, web scopes, local \`cwd\`, and display labels, so local paths and path fragments work.
|
|
4040
|
-
- Useful filters include \`--provider <provider>\`, \`--since 30d\`, and \`--repo "<repo-or-path>"\`. Provider aliases are ordered as OpenAI (\`codex-cli\`, \`codex-desktop\`, \`chatgpt\`), Anthropic (\`claude\`, \`claude-code-desktop\`, \`claude-workspace\`, \`claude-web\`, \`claude-sdk\`), Google (\`gemini-cli\`, \`antigravity\`), then other local tools (\`devin-cli\`, \`cursor\`, \`cline\`, \`opencode\`, \`aider\`).
|
|
4067
|
+
- Useful filters include \`--provider <provider>\`, \`--since 30d\`, and \`--repo "<repo-or-path>"\`. Provider aliases are ordered as OpenAI (\`codex-cli\`, \`codex-desktop\`, \`codex-sdk\`, \`chatgpt\`), Anthropic (\`claude\`, \`claude-code-desktop\`, \`claude-workspace\`, \`claude-web\`, \`claude-sdk\`), Google (\`gemini-cli\`, \`antigravity\`), then other local tools (\`devin-cli\`, \`cursor\`, \`cline\`, \`opencode\`, \`aider\`).
|
|
4041
4068
|
- If the user is asking about the current repository, start without \`--repo\` unless results are noisy; current-repo matches are already weighted higher.`;
|
|
4042
4069
|
}
|
|
4043
4070
|
|
|
@@ -4305,6 +4332,10 @@ function printDiscovery(label, result) {
|
|
|
4305
4332
|
|
|
4306
4333
|
function printImportResults(results, options = {}) {
|
|
4307
4334
|
for (const result of results) {
|
|
4335
|
+
if (result.instructions) {
|
|
4336
|
+
printWebExportInstructionBlock(result.instructions);
|
|
4337
|
+
continue;
|
|
4338
|
+
}
|
|
4308
4339
|
const detailText = result.details ? formatDetails(result.details) : "";
|
|
4309
4340
|
const details = detailText ? ` ${detailText}` : "";
|
|
4310
4341
|
printCheck(
|
|
@@ -4326,6 +4357,37 @@ function printImportResults(results, options = {}) {
|
|
|
4326
4357
|
}
|
|
4327
4358
|
}
|
|
4328
4359
|
|
|
4360
|
+
function printWebExportInstructions(instructions, flags = {}) {
|
|
4361
|
+
if (!instructions) throw new Error("unknown web export instruction source");
|
|
4362
|
+
if (flags.json) {
|
|
4363
|
+
console.log(JSON.stringify({
|
|
4364
|
+
provider: instructions.provider,
|
|
4365
|
+
source: instructions.source,
|
|
4366
|
+
manual: true,
|
|
4367
|
+
instructions
|
|
4368
|
+
}, null, 2));
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4371
|
+
printPageTitle("agentlog import", `${instructions.source} export instructions`);
|
|
4372
|
+
printWebExportInstructionBlock(instructions);
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
function printWebExportInstructionBlock(instructions) {
|
|
4376
|
+
printSection(`${instructions.label} Export`);
|
|
4377
|
+
printMuted(`Request and download the ${instructions.fileDescription}, then import it from disk.`);
|
|
4378
|
+
printCheck("Request page", instructions.requestUrl);
|
|
4379
|
+
printCheck("Help", instructions.helpUrl);
|
|
4380
|
+
printSection("Steps");
|
|
4381
|
+
(instructions.steps || []).forEach((step, index) => {
|
|
4382
|
+
console.log(` ${index + 1}. ${step}`);
|
|
4383
|
+
});
|
|
4384
|
+
if (instructions.notes?.length) {
|
|
4385
|
+
printSection("Notes");
|
|
4386
|
+
for (const note of instructions.notes) console.log(` - ${note}`);
|
|
4387
|
+
}
|
|
4388
|
+
printCommand("Import after download", instructions.importCommand);
|
|
4389
|
+
}
|
|
4390
|
+
|
|
4329
4391
|
function printPageTitle(title, subtitle = "") {
|
|
4330
4392
|
if (!process.stdout.isTTY) {
|
|
4331
4393
|
console.log(title);
|
|
@@ -5089,6 +5151,14 @@ function importSourceOptions(discovered) {
|
|
|
5089
5151
|
description: "Codex desktop app conversations from the local Codex state database.",
|
|
5090
5152
|
defaultSelected: Boolean(discovered.codexDesktop?.sessions)
|
|
5091
5153
|
},
|
|
5154
|
+
{
|
|
5155
|
+
source: "codex-sdk",
|
|
5156
|
+
label: "Codex SDK jobs",
|
|
5157
|
+
count: discovered.codexSdk?.sessions || 0,
|
|
5158
|
+
summary: sourceSummary(discovered.codexSdk),
|
|
5159
|
+
description: "Codex exec and SDK-style batch runs from the local Codex state database; disabled by default because volume can be high.",
|
|
5160
|
+
defaultSelected: false
|
|
5161
|
+
},
|
|
5092
5162
|
{
|
|
5093
5163
|
source: "claude",
|
|
5094
5164
|
label: "Claude Code CLI",
|
|
@@ -5607,6 +5677,12 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5607
5677
|
.unsupported-device-block:last-child{margin-bottom:0}
|
|
5608
5678
|
.unsupported-device-block:before{content:"";width:6px;height:6px;border-radius:999px;background:#94a3b8;flex:0 0 auto}
|
|
5609
5679
|
.context-card{margin:4px 0;border:1px solid #e5e7eb;border-left:3px solid #cbd5e1;border-radius:8px;background:#fff;color:#475569;overflow:hidden;transition:border-color .15s ease}
|
|
5680
|
+
.context-line{display:flex;align-items:center;gap:8px;min-height:28px;margin:2px 0;color:#64748b;font-size:12px;line-height:1.35}
|
|
5681
|
+
.context-line .context-glyph{width:22px;height:22px}
|
|
5682
|
+
.context-line .context-title{flex:0 0 auto}
|
|
5683
|
+
.context-line .context-meta{flex:1 1 auto}
|
|
5684
|
+
.context-line .context-end{margin-left:auto}
|
|
5685
|
+
.context-line .message-copy{opacity:1;width:22px;height:22px;color:#94a3b8}
|
|
5610
5686
|
.session-summary-message{margin-bottom:14px}
|
|
5611
5687
|
.session-summary-card{border-left-color:#D97757;background:#fff}
|
|
5612
5688
|
.session-summary-body{padding:11px 13px 12px;color:#334155;font-size:13px;line-height:1.55;background:#fff}
|
|
@@ -5648,13 +5724,31 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5648
5724
|
.md-table{border-collapse:collapse;width:max-content;max-width:100%;font-size:13px}
|
|
5649
5725
|
.md-table th,.md-table td{border:1px solid #e5e7eb;padding:5px 7px;text-align:left;vertical-align:top}
|
|
5650
5726
|
.md-table th{background:#f8fafc;font-weight:600}
|
|
5651
|
-
.tool-
|
|
5652
|
-
.tool-
|
|
5653
|
-
.tool-
|
|
5727
|
+
.tool-group-card{margin:0;border:0;background:transparent}
|
|
5728
|
+
.tool-group-card > summary{display:flex;align-items:center;gap:8px;min-height:28px;margin:0 0 5px;color:#64748b;font-size:13px;font-weight:600;line-height:1.35;cursor:pointer;list-style:none}
|
|
5729
|
+
.tool-group-card > summary::-webkit-details-marker{display:none}
|
|
5730
|
+
.tool-group-prefix{display:inline-flex;align-items:center;gap:7px;flex:0 0 auto}
|
|
5731
|
+
.tool-group-caret{width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:5px solid #94a3b8;transition:transform .15s ease}
|
|
5732
|
+
.tool-group-card[open] .tool-group-caret{transform:rotate(90deg)}
|
|
5733
|
+
.tool-group-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
5734
|
+
.tool-group-card[open] > .tool-stack{gap:0;border:1px solid #e5e7eb;border-radius:8px;background:#fff;overflow:hidden}
|
|
5735
|
+
.tool-group-card[open] > .tool-stack > .tool-callout{border:0;border-radius:0;border-bottom:1px solid #eef2f7}
|
|
5736
|
+
.tool-group-card[open] > .tool-stack > .tool-callout:last-child{border-bottom:0}
|
|
5737
|
+
.tool-group-card[open] > .tool-stack > .tool-callout > summary{min-height:26px;padding:2px 8px}
|
|
5738
|
+
.tool-callout{display:block;margin:0;border:1px solid #e5e7eb;border-radius:8px;background:#fff;color:#0f172a;overflow:hidden;transition:border-color .15s ease,background .15s ease}
|
|
5739
|
+
.tool-callout > summary{display:flex;align-items:center;gap:7px;min-height:28px;padding:3px 8px;cursor:pointer;list-style:none;transition:background .12s ease}
|
|
5740
|
+
.tool-callout > summary::-webkit-details-marker{display:none}
|
|
5741
|
+
.tool-callout > summary:before{content:"";flex:0 0 auto;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:5px solid #94a3b8;transition:transform .15s ease}
|
|
5742
|
+
.tool-callout[open] > summary:before{transform:rotate(90deg)}
|
|
5743
|
+
.tool-callout:hover,.tool-callout[open]{border-color:#dbe3ef}
|
|
5744
|
+
.tool-callout > summary:hover{background:#fafbfc}
|
|
5745
|
+
.tool-callout[open] > summary{border-bottom:1px solid #f1f5f9}
|
|
5746
|
+
.tool-stack{display:grid;gap:3px;margin-top:3px}
|
|
5654
5747
|
.tool-stack:first-child{margin-top:0}
|
|
5655
5748
|
.tool-body + .tool-stack{margin-top:8px}
|
|
5656
|
-
.tool-
|
|
5657
|
-
.tool-glyph
|
|
5749
|
+
.tool-stack-heading{display:flex;align-items:center;min-height:24px;margin:0 0 1px;color:#64748b;font-size:12px;font-weight:600;line-height:1.35}
|
|
5750
|
+
.tool-glyph{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:22px;height:22px;border-radius:6px;background:#f1f5f9;color:#475569;border:0}
|
|
5751
|
+
.tool-glyph svg{width:13px;height:13px}
|
|
5658
5752
|
.tool-callout.shell .tool-glyph{background:#f1f5f9;color:#334155}
|
|
5659
5753
|
.tool-callout.edit .tool-glyph{background:#fef3c7;color:#92400e}
|
|
5660
5754
|
.tool-callout.read .tool-glyph{background:#e0f2fe;color:#075985}
|
|
@@ -5663,16 +5757,21 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5663
5757
|
.tool-callout.task .tool-glyph{background:#dcfce7;color:#166534}
|
|
5664
5758
|
.tool-callout.mcp .tool-glyph{background:#ffedd5;color:#9a3412}
|
|
5665
5759
|
.tool-callout.skill .tool-glyph{background:#e0e7ff;color:#3730a3}
|
|
5666
|
-
.tool-
|
|
5667
|
-
.tool-
|
|
5760
|
+
.tool-call-line{display:flex;align-items:baseline;gap:5px;min-width:0;flex:1 1 auto}
|
|
5761
|
+
.tool-action{flex:0 0 auto;color:#0f172a;font-size:12.5px;font-weight:600;line-height:1.3}
|
|
5762
|
+
.tool-subject{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#475569;font-size:12.5px;font-weight:500;line-height:1.3}
|
|
5763
|
+
.tool-callout.shell .tool-subject{font:12px/1.3 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:#475569}
|
|
5764
|
+
.tool-callout-body{display:grid;gap:6px;min-width:0;padding:6px 8px 8px 12px;background:#fff}
|
|
5765
|
+
.tool-call-meta{display:flex;align-items:center;gap:5px;flex-wrap:wrap;min-width:0}
|
|
5668
5766
|
.tool-chip{display:inline-flex;align-items:center;height:18px;padding:0 7px;border-radius:999px;background:#f1f5f9;color:#475569;border:0;font-size:11px;font-weight:500;letter-spacing:.01em}
|
|
5669
5767
|
.tool-status{display:inline-flex;align-items:center;height:18px;border-radius:999px;padding:0 7px;background:#f1f5f9;color:#64748b;border:0;font-size:11px;font-weight:600}
|
|
5670
5768
|
.tool-status.completed{background:#dcfce7;color:#166534}
|
|
5671
5769
|
.tool-status.pending{background:#fef9c3;color:#854d0e}
|
|
5672
5770
|
.tool-status.failed,.tool-status.error{background:#fee2e2;color:#991b1b}
|
|
5673
|
-
.tool-
|
|
5674
|
-
.tool-
|
|
5675
|
-
.tool-
|
|
5771
|
+
.tool-preview{display:block;margin:0;padding:6px 7px;border:1px solid #edf2f7;border-radius:7px;background:#fafbfc;color:#1e293b;overflow-x:hidden;overflow-y:visible;font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}
|
|
5772
|
+
.tool-paired-result{display:grid;gap:4px;min-width:0}
|
|
5773
|
+
.tool-result-meta{display:flex;align-items:center;gap:8px;min-width:0;color:#64748b;font-size:11px;font-weight:600}
|
|
5774
|
+
.tool-result-meta span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
5676
5775
|
.tool-diff{display:block;margin-top:6px;border:1px solid #e5e7eb;border-radius:6px;background:#fff;overflow:hidden;font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
|
|
5677
5776
|
.tool-diff[open] .tool-diff-summary{border-bottom:1px solid #f1f5f9}
|
|
5678
5777
|
.tool-diff-summary{display:flex;align-items:center;gap:8px;min-height:28px;padding:4px 10px;cursor:pointer;color:#475569;font-size:11px;font-weight:600;list-style:none;background:#fafbfc}
|
|
@@ -5681,24 +5780,28 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5681
5780
|
.tool-diff[open] .tool-diff-summary:before{transform:rotate(90deg)}
|
|
5682
5781
|
.tool-diff-summary .add-count{color:#16a34a}
|
|
5683
5782
|
.tool-diff-summary .del-count{color:#dc2626}
|
|
5684
|
-
.tool-diff-body{display:block;
|
|
5783
|
+
.tool-diff-body{display:block;overflow-x:hidden;overflow-y:visible;background:#f8fafc}
|
|
5685
5784
|
.tool-diff-block{display:block}
|
|
5686
5785
|
.tool-diff-block + .tool-diff-block{border-top:1px solid #e5e7eb;margin-top:4px;padding-top:4px}
|
|
5687
|
-
.tool-diff-line{display:block;padding:0 10px;white-space:pre-wrap;overflow-wrap:anywhere;color:#0f172a}
|
|
5786
|
+
.tool-diff-line{display:block;padding:0 10px;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;color:#0f172a}
|
|
5688
5787
|
.tool-diff-line.add{background:#e6ffec;color:#1a7f37}
|
|
5689
5788
|
.tool-diff-line.del{background:#ffebe9;color:#a40e26}
|
|
5690
5789
|
.tool-diff-line.ctx{color:#6b7280}
|
|
5691
5790
|
.tool-diff-line.meta{color:#6b7280;font-weight:600;background:#f1f5f9}
|
|
5692
5791
|
.tool-diff-line.hunk{color:#7c3aed;background:#faf5ff;font-weight:600}
|
|
5693
5792
|
.tool-result{margin:0;border:1px solid #e5e7eb;border-radius:8px;background:#fff;overflow:hidden}
|
|
5793
|
+
.tool-result.inline{margin-top:1px;border-radius:7px;background:#f8fafc}
|
|
5694
5794
|
.tool-result summary{display:flex;align-items:center;gap:8px;min-height:34px;padding:6px 10px 6px 11px;cursor:pointer;color:#0f172a;font-weight:600;list-style:none;transition:background .12s ease}
|
|
5795
|
+
.tool-result.inline summary{min-height:30px;padding:5px 9px;background:#fafbfc}
|
|
5695
5796
|
.tool-result summary:hover{background:#fafbfc}
|
|
5797
|
+
.tool-result.inline summary:hover{background:#f1f5f9}
|
|
5696
5798
|
.tool-result[open] summary{border-bottom:1px solid #f1f5f9}
|
|
5697
5799
|
.tool-result summary::-webkit-details-marker{display:none}
|
|
5698
5800
|
.tool-result summary:before{content:"";flex:0 0 auto;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:5px solid #94a3b8;transition:transform .15s ease}
|
|
5699
5801
|
.tool-result[open] summary:before{transform:rotate(90deg)}
|
|
5700
5802
|
.tool-result-kind{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;color:#0f172a;letter-spacing:-0.005em;flex:0 0 auto;white-space:nowrap}
|
|
5701
|
-
.tool-result-kind .tool-glyph{width:
|
|
5803
|
+
.tool-result-kind .tool-glyph{width:20px;height:20px;border-radius:6px}
|
|
5804
|
+
.tool-result.inline .tool-result-kind .tool-glyph{width:20px;height:20px}
|
|
5702
5805
|
.tool-result-kind .tool-glyph svg{width:12px;height:12px}
|
|
5703
5806
|
.tool-result[data-category="shell"] .tool-result-kind .tool-glyph{background:#f1f5f9;color:#334155}
|
|
5704
5807
|
.tool-result[data-category="edit"] .tool-result-kind .tool-glyph{background:#fef3c7;color:#92400e}
|
|
@@ -5709,7 +5812,12 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5709
5812
|
.tool-result[data-category="mcp"] .tool-result-kind .tool-glyph{background:#ffedd5;color:#9a3412}
|
|
5710
5813
|
.tool-result-detail{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#64748b;font-size:12px;font-weight:500}
|
|
5711
5814
|
.tool-result-count{margin-left:auto;flex:0 0 auto;color:#94a3b8;font-size:11px;font-weight:600;letter-spacing:.01em;white-space:nowrap}
|
|
5712
|
-
.tool-output{max-width:100%;
|
|
5815
|
+
.tool-output{max-width:100%;margin:0;padding:6px 7px;background:#f8fafc;color:#0f172a;overflow-x:hidden;overflow-y:visible;font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}
|
|
5816
|
+
.tool-result.inline .tool-output{background:#fff}
|
|
5817
|
+
.tool-output-lines{display:grid;gap:0;padding:5px 0}
|
|
5818
|
+
.tool-output-line{display:grid;grid-template-columns:minmax(2.4em,max-content) minmax(0,1fr);gap:10px;min-width:0;padding:0 7px}
|
|
5819
|
+
.tool-line-number{color:#94a3b8;text-align:right;user-select:none}
|
|
5820
|
+
.tool-line-text{min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}
|
|
5713
5821
|
.skill-link{display:inline-flex;align-items:center;gap:4px;max-width:100%;vertical-align:middle;border:1px solid #c7d2fe;border-radius:999px;background:#eef2ff;color:#1e1b4b;padding:1px 7px 2px;font-size:.93em;line-height:1.35;white-space:nowrap}
|
|
5714
5822
|
.skill-mark{font:700 11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:#4338ca}
|
|
5715
5823
|
.skill-name,.skill-path{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
@@ -5750,6 +5858,7 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5750
5858
|
<button class="select-option active" type="button" data-value="">All sources</button>
|
|
5751
5859
|
<button class="select-option" type="button" data-value="codex-cli">Codex CLI</button>
|
|
5752
5860
|
<button class="select-option" type="button" data-value="codex-desktop">Codex Desktop</button>
|
|
5861
|
+
<button class="select-option" type="button" data-value="codex-sdk">Codex SDK jobs</button>
|
|
5753
5862
|
<button class="select-option" type="button" data-value="chatgpt">ChatGPT</button>
|
|
5754
5863
|
<button class="select-option" type="button" data-value="claude">Claude Code CLI</button>
|
|
5755
5864
|
<button class="select-option" type="button" data-value="claude-code-desktop">Claude Code Desktop</button>
|
|
@@ -5879,6 +5988,11 @@ mark.search-match.search-match-current{background:#fde047;color:#422006;box-shad
|
|
|
5879
5988
|
<div id="statsChatActivitySub" class="stats-card-meta" hidden></div>
|
|
5880
5989
|
<div id="statsChatHeatmap" class="stats-heatmap-wrap"></div>
|
|
5881
5990
|
</div>
|
|
5991
|
+
<div class="stats-card stats-card--compact">
|
|
5992
|
+
<div class="stats-card-title">SDK jobs</div>
|
|
5993
|
+
<div id="statsSdkActivitySub" class="stats-card-meta" hidden></div>
|
|
5994
|
+
<div id="statsSdkHeatmap" class="stats-heatmap-wrap"></div>
|
|
5995
|
+
</div>
|
|
5882
5996
|
</div>
|
|
5883
5997
|
<div class="stats-section">
|
|
5884
5998
|
<div class="stats-section-head stats-section-head--breakdown">
|
|
@@ -6033,7 +6147,7 @@ function brandIconSvg(kind, className) {
|
|
|
6033
6147
|
|
|
6034
6148
|
function brandKeyForSourceValue(value) {
|
|
6035
6149
|
const key = String(value || '').trim().toLowerCase();
|
|
6036
|
-
if (['codex-cli', 'codex-desktop', 'chatgpt'].includes(key)) return 'openai';
|
|
6150
|
+
if (['codex-cli', 'codex-desktop', 'codex-sdk', 'chatgpt'].includes(key)) return 'openai';
|
|
6037
6151
|
if (['claude', 'claude-code-desktop', 'claude-workspace', 'claude-web', 'claude-sdk'].includes(key)) return 'claude';
|
|
6038
6152
|
if (['gemini-cli', 'antigravity'].includes(key)) return 'gemini';
|
|
6039
6153
|
if (key === 'devin-cli') return 'devin';
|
|
@@ -6360,8 +6474,8 @@ function companyColor(company) {
|
|
|
6360
6474
|
}
|
|
6361
6475
|
|
|
6362
6476
|
const MODEL_COLOR_PALETTES = {
|
|
6363
|
-
openai: { family: 'openai', light: '#
|
|
6364
|
-
claude: { family: 'claude', light: '#
|
|
6477
|
+
openai: { family: 'openai', light: '#BAE6FD', base: '#06B6D4', dark: PROVIDER_COLORS.codex, darker: '#1D4ED8', top: '#172554' },
|
|
6478
|
+
claude: { family: 'claude', light: '#FED7AA', base: '#FB923C', dark: PROVIDER_COLORS.claude_code, darker: '#9A3412', top: '#831843' },
|
|
6365
6479
|
gemini: { family: 'gemini', light: '#C4B5FD', base: PROVIDER_COLORS.gemini_cli, dark: '#7E22CE', darker: '#581C87' },
|
|
6366
6480
|
devin: { family: 'devin', light: '#5EEAD4', base: PROVIDER_COLORS.devin, dark: '#0F766E', darker: '#115E59' },
|
|
6367
6481
|
cursor: { family: 'cursor', light: '#475569', base: PROVIDER_COLORS.cursor, dark: '#020617', darker: '#020617' },
|
|
@@ -6409,6 +6523,9 @@ function modelCapabilityColor(text, palette) {
|
|
|
6409
6523
|
}
|
|
6410
6524
|
|
|
6411
6525
|
function openAiModelColor(text, palette) {
|
|
6526
|
+
if (text.includes('gpt-5.5') || text.includes('gpt-5-5')) return palette.top;
|
|
6527
|
+
if (text.includes('gpt-5.4') || text.includes('gpt-5-4')) return palette.darker;
|
|
6528
|
+
if (text.includes('gpt-5.3') || text.includes('gpt-5-3')) return palette.dark;
|
|
6412
6529
|
if (text.includes('gpt-5.1') || text.includes('gpt-5-1')) return palette.top;
|
|
6413
6530
|
if (text.includes('gpt-5') || modelHasToken(text, ['o3'])) return palette.darker;
|
|
6414
6531
|
if (text.includes('gpt-4.1') || modelHasToken(text, ['o4'])) return palette.dark;
|
|
@@ -6417,10 +6534,12 @@ function openAiModelColor(text, palette) {
|
|
|
6417
6534
|
}
|
|
6418
6535
|
|
|
6419
6536
|
function claudeModelColor(text, palette) {
|
|
6420
|
-
if (text.includes('high-thinking') || text.includes('high_thinking')) return palette.top;
|
|
6421
|
-
if (modelHasToken(text, ['opus'])) return palette.darker;
|
|
6422
|
-
if (modelHasToken(text, ['sonnet'])) return palette.dark;
|
|
6423
6537
|
if (modelHasToken(text, ['haiku'])) return palette.light;
|
|
6538
|
+
if (text.includes('claude-4.6') && modelHasToken(text, ['opus'])) return palette.top;
|
|
6539
|
+
if (text.includes('claude-4.5') && modelHasToken(text, ['opus'])) return palette.darker;
|
|
6540
|
+
if (text.includes('high-thinking') || text.includes('high_thinking')) return palette.darker;
|
|
6541
|
+
if (modelHasToken(text, ['opus'])) return palette.dark;
|
|
6542
|
+
if (modelHasToken(text, ['sonnet'])) return palette.base;
|
|
6424
6543
|
return palette.base;
|
|
6425
6544
|
}
|
|
6426
6545
|
|
|
@@ -6429,6 +6548,91 @@ function modelHasToken(text, tokens) {
|
|
|
6429
6548
|
return new RegExp('(^|[^a-z0-9])(' + escaped + ')([^a-z0-9]|$)').test(text);
|
|
6430
6549
|
}
|
|
6431
6550
|
|
|
6551
|
+
const CANONICAL_COMPANY_ORDER = ['openai', 'anthropic', 'google', 'cognition', 'cursor', 'stealth', 'unknown'];
|
|
6552
|
+
const CANONICAL_PROVIDER_ORDER = ['codex', 'chatgpt', 'claude_code', 'claude_desktop', 'claude_sdk', 'claude_web', 'gemini_cli', 'antigravity', 'devin', 'windsurf', 'cursor', 'cline', 'opencode', 'aider', 'unknown'];
|
|
6553
|
+
|
|
6554
|
+
function canonicalCompanyRank(company) {
|
|
6555
|
+
const idx = CANONICAL_COMPANY_ORDER.indexOf(String(company || '').toLowerCase());
|
|
6556
|
+
return idx < 0 ? CANONICAL_COMPANY_ORDER.length : idx;
|
|
6557
|
+
}
|
|
6558
|
+
|
|
6559
|
+
function canonicalProviderRank(provider) {
|
|
6560
|
+
const idx = CANONICAL_PROVIDER_ORDER.indexOf(String(provider || '').toLowerCase());
|
|
6561
|
+
return idx < 0 ? CANONICAL_PROVIDER_ORDER.length : idx;
|
|
6562
|
+
}
|
|
6563
|
+
|
|
6564
|
+
function companyForModel(text) {
|
|
6565
|
+
const value = String(text || '').toLowerCase();
|
|
6566
|
+
if (value.startsWith('composer-') || value.startsWith('cursor-') || value === 'auto' || value === 'default') return 'cursor';
|
|
6567
|
+
const palette = modelFamilyPalette(value);
|
|
6568
|
+
if (!palette) return 'unknown';
|
|
6569
|
+
if (palette.family === 'openai') return 'openai';
|
|
6570
|
+
if (palette.family === 'claude') return 'anthropic';
|
|
6571
|
+
if (palette.family === 'gemini' || palette.family === 'antigravity') return 'google';
|
|
6572
|
+
if (palette.family === 'devin' || palette.family === 'windsurf') return 'cognition';
|
|
6573
|
+
if (palette.family === 'cursor') return 'cursor';
|
|
6574
|
+
return 'unknown';
|
|
6575
|
+
}
|
|
6576
|
+
|
|
6577
|
+
function canonicalModelRank(model) {
|
|
6578
|
+
const text = String(model || '').toLowerCase();
|
|
6579
|
+
const company = companyForModel(text);
|
|
6580
|
+
const companyRank = canonicalCompanyRank(company);
|
|
6581
|
+
let withinRank = 950;
|
|
6582
|
+
if (company === 'openai') {
|
|
6583
|
+
if (modelHasToken(text, ['nano'])) withinRank = 920;
|
|
6584
|
+
else if (modelHasToken(text, ['mini'])) withinRank = 900;
|
|
6585
|
+
else if (text.includes('gpt-4') && !text.includes('gpt-4.1')) withinRank = 700;
|
|
6586
|
+
else if (text.includes('gpt-4.1')) withinRank = 600;
|
|
6587
|
+
else if (modelHasToken(text, ['o1'])) withinRank = 590;
|
|
6588
|
+
else if (modelHasToken(text, ['o3', 'o4'])) withinRank = 580;
|
|
6589
|
+
else if (text.includes('gpt-5.5') || text.includes('gpt-5-5')) withinRank = 100;
|
|
6590
|
+
else if (text.includes('gpt-5.4') || text.includes('gpt-5-4')) withinRank = 200;
|
|
6591
|
+
else if (text.includes('gpt-5.3') || text.includes('gpt-5-3')) withinRank = 300;
|
|
6592
|
+
else if (text.includes('gpt-5.1') || text.includes('gpt-5-1')) withinRank = 400;
|
|
6593
|
+
else if (text.includes('gpt-5')) withinRank = 500;
|
|
6594
|
+
} else if (company === 'anthropic') {
|
|
6595
|
+
if (modelHasToken(text, ['haiku'])) withinRank = 920;
|
|
6596
|
+
else if (text.includes('opus-4-7') || text.includes('claude-4.7')) withinRank = 100;
|
|
6597
|
+
else if (text.includes('claude-4.6') && modelHasToken(text, ['opus'])) withinRank = 200;
|
|
6598
|
+
else if (text.includes('claude-4.5') && modelHasToken(text, ['opus'])) withinRank = 300;
|
|
6599
|
+
else if (text.includes('claude-4') && modelHasToken(text, ['opus'])) withinRank = 400;
|
|
6600
|
+
else if (modelHasToken(text, ['opus'])) withinRank = 500;
|
|
6601
|
+
else if (text.includes('claude-4.5') && modelHasToken(text, ['sonnet'])) withinRank = 600;
|
|
6602
|
+
else if (text.includes('claude-4') && modelHasToken(text, ['sonnet'])) withinRank = 700;
|
|
6603
|
+
else if (modelHasToken(text, ['sonnet'])) withinRank = 800;
|
|
6604
|
+
} else if (company === 'google') {
|
|
6605
|
+
// Major version dictates the bucket (newer first); flash variants get +50 within their generation.
|
|
6606
|
+
let base = 700;
|
|
6607
|
+
if (text.includes('gemini-3')) base = 100;
|
|
6608
|
+
else if (text.includes('gemini-2')) base = 300;
|
|
6609
|
+
else if (text.includes('gemini-1.5')) base = 500;
|
|
6610
|
+
else if (text.includes('gemini-1')) base = 600;
|
|
6611
|
+
let modifier = 0;
|
|
6612
|
+
if (text.includes('flash-lite')) modifier = 80;
|
|
6613
|
+
else if (text.includes('flash')) modifier = 50;
|
|
6614
|
+
withinRank = base + modifier;
|
|
6615
|
+
} else if (company === 'cursor') {
|
|
6616
|
+
if (text === 'composer-1.5') withinRank = 100;
|
|
6617
|
+
else if (text === 'composer-1') withinRank = 200;
|
|
6618
|
+
else if (text.startsWith('composer-')) withinRank = 300;
|
|
6619
|
+
else if (text === 'auto') withinRank = 700;
|
|
6620
|
+
else if (text === 'default') withinRank = 800;
|
|
6621
|
+
else if (text.startsWith('cursor-')) withinRank = 600;
|
|
6622
|
+
}
|
|
6623
|
+
return companyRank * 10000 + withinRank;
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6626
|
+
function canonicalGroupRank(group) {
|
|
6627
|
+
if (statsBreakdownMode === 'model') return canonicalModelRank(group);
|
|
6628
|
+
if (statsBreakdownMode === 'company') return canonicalCompanyRank(group);
|
|
6629
|
+
return canonicalProviderRank(group);
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
function statsCanonicalOrderedGroups(groups) {
|
|
6633
|
+
return [...groups].sort((a, b) => canonicalGroupRank(a) - canonicalGroupRank(b) || String(a).localeCompare(String(b)));
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6432
6636
|
function statsBreakdownLabel(group) {
|
|
6433
6637
|
if (statsBreakdownMode === 'model') return modelLabel(group);
|
|
6434
6638
|
if (statsBreakdownMode === 'company') return companyLabel(group);
|
|
@@ -7129,7 +7333,8 @@ function renderMessages(messages, sessionSummary) {
|
|
|
7129
7333
|
const visibleMessages = summaryText
|
|
7130
7334
|
? (messages || []).filter((message) => message?.metadata?.summaryKind !== 'conversation_summary')
|
|
7131
7335
|
: (messages || []);
|
|
7132
|
-
|
|
7336
|
+
const renderItems = pairedToolRenderItems(visibleMessages);
|
|
7337
|
+
if (!renderItems.length && !summaryText) {
|
|
7133
7338
|
readableView.className = 'inline-empty';
|
|
7134
7339
|
readableView.textContent = 'No transcript messages are available for this session.';
|
|
7135
7340
|
return;
|
|
@@ -7144,16 +7349,17 @@ function renderMessages(messages, sessionSummary) {
|
|
|
7144
7349
|
const renderChunk = () => {
|
|
7145
7350
|
if (renderSerial !== renderMessagesSerial) return;
|
|
7146
7351
|
const fragment = document.createDocumentFragment();
|
|
7147
|
-
const end = Math.min(
|
|
7352
|
+
const end = Math.min(renderItems.length, index + MESSAGE_RENDER_CHUNK_SIZE);
|
|
7148
7353
|
for (; index < end; index++) {
|
|
7149
|
-
const
|
|
7354
|
+
const item = renderItems[index];
|
|
7355
|
+
const message = item.message;
|
|
7150
7356
|
const gap = renderTimeGap(previousTimestamp, message.timestamp);
|
|
7151
7357
|
if (gap) fragment.appendChild(gap);
|
|
7152
|
-
fragment.appendChild(messageElement(message));
|
|
7153
|
-
|
|
7358
|
+
fragment.appendChild(messageElement(message, item));
|
|
7359
|
+
previousTimestamp = item.lastTimestamp || message.timestamp || previousTimestamp;
|
|
7154
7360
|
}
|
|
7155
7361
|
readableView.appendChild(fragment);
|
|
7156
|
-
if (index <
|
|
7362
|
+
if (index < renderItems.length) {
|
|
7157
7363
|
scheduleNext(renderChunk);
|
|
7158
7364
|
return;
|
|
7159
7365
|
}
|
|
@@ -7168,6 +7374,185 @@ function renderMessages(messages, sessionSummary) {
|
|
|
7168
7374
|
renderChunk();
|
|
7169
7375
|
}
|
|
7170
7376
|
|
|
7377
|
+
function pairedToolRenderItems(messages) {
|
|
7378
|
+
const pairings = pairedToolResultIndexes(messages);
|
|
7379
|
+
const skippedResultIndexes = new Set(pairings.duplicateResultIndexes);
|
|
7380
|
+
const result = [];
|
|
7381
|
+
for (let index = 0; index < messages.length; index++) {
|
|
7382
|
+
if (skippedResultIndexes.has(index)) continue;
|
|
7383
|
+
const message = messages[index];
|
|
7384
|
+
const calls = messageToolCalls(message);
|
|
7385
|
+
if (!calls.length) {
|
|
7386
|
+
result.push({ type: 'message', message });
|
|
7387
|
+
continue;
|
|
7388
|
+
}
|
|
7389
|
+
const pairedResults = (pairings.byCallMessage.get(index) || [])
|
|
7390
|
+
.filter((resultIndex) => !skippedResultIndexes.has(resultIndex))
|
|
7391
|
+
.map((resultIndex) => {
|
|
7392
|
+
skippedResultIndexes.add(resultIndex);
|
|
7393
|
+
return messages[resultIndex];
|
|
7394
|
+
});
|
|
7395
|
+
let next = index + 1;
|
|
7396
|
+
while (pairedResults.length < calls.length && next < messages.length && isPairableToolResultMessage(messages[next])) {
|
|
7397
|
+
if (skippedResultIndexes.has(next)) {
|
|
7398
|
+
next += 1;
|
|
7399
|
+
continue;
|
|
7400
|
+
}
|
|
7401
|
+
pairedResults.push(messages[next]);
|
|
7402
|
+
skippedResultIndexes.add(next);
|
|
7403
|
+
next += 1;
|
|
7404
|
+
}
|
|
7405
|
+
if (pairedResults.length) {
|
|
7406
|
+
result.push({
|
|
7407
|
+
type: 'message',
|
|
7408
|
+
message,
|
|
7409
|
+
pairedToolResults: pairedResults,
|
|
7410
|
+
lastTimestamp: pairedResults[pairedResults.length - 1].timestamp || message.timestamp
|
|
7411
|
+
});
|
|
7412
|
+
index = next - 1;
|
|
7413
|
+
} else {
|
|
7414
|
+
result.push({ type: 'message', message });
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
return groupConsecutiveToolItems(result);
|
|
7418
|
+
}
|
|
7419
|
+
|
|
7420
|
+
function groupConsecutiveToolItems(items) {
|
|
7421
|
+
const grouped = [];
|
|
7422
|
+
let run = [];
|
|
7423
|
+
const flush = () => {
|
|
7424
|
+
if (run.length > 1) {
|
|
7425
|
+
grouped.push({
|
|
7426
|
+
type: 'tool-group',
|
|
7427
|
+
message: run[0].message,
|
|
7428
|
+
items: run,
|
|
7429
|
+
lastTimestamp: run[run.length - 1].lastTimestamp || run[run.length - 1].message.timestamp || run[0].message.timestamp
|
|
7430
|
+
});
|
|
7431
|
+
} else if (run.length === 1) {
|
|
7432
|
+
grouped.push(run[0]);
|
|
7433
|
+
}
|
|
7434
|
+
run = [];
|
|
7435
|
+
};
|
|
7436
|
+
for (const item of items) {
|
|
7437
|
+
if (!isToolActivityRenderItem(item)) {
|
|
7438
|
+
flush();
|
|
7439
|
+
grouped.push(item);
|
|
7440
|
+
continue;
|
|
7441
|
+
}
|
|
7442
|
+
run.push(item);
|
|
7443
|
+
}
|
|
7444
|
+
flush();
|
|
7445
|
+
return grouped;
|
|
7446
|
+
}
|
|
7447
|
+
|
|
7448
|
+
function isToolActivityRenderItem(item) {
|
|
7449
|
+
if (!item || item.type !== 'message') return false;
|
|
7450
|
+
const message = item.message || {};
|
|
7451
|
+
const role = String(message.role || '').toLowerCase();
|
|
7452
|
+
if (role === 'tool') return Boolean(parseToolResult(message.content || '', message));
|
|
7453
|
+
if (role !== 'assistant') return false;
|
|
7454
|
+
const calls = messageToolCalls(message, item.pairedToolResults || []);
|
|
7455
|
+
if (!calls.length) return false;
|
|
7456
|
+
return !stripToolInvocationLines(message.content || '').trim();
|
|
7457
|
+
}
|
|
7458
|
+
|
|
7459
|
+
function pairedToolResultIndexes(messages) {
|
|
7460
|
+
const calls = [];
|
|
7461
|
+
const callsByEventId = new Map();
|
|
7462
|
+
const callsById = new Map();
|
|
7463
|
+
const callsByKind = new Map();
|
|
7464
|
+
const addCallKey = (map, key, callIndex) => {
|
|
7465
|
+
if (!key) return;
|
|
7466
|
+
if (!map.has(key)) map.set(key, []);
|
|
7467
|
+
map.get(key).push(callIndex);
|
|
7468
|
+
};
|
|
7469
|
+
for (let messagePosition = 0; messagePosition < messages.length; messagePosition++) {
|
|
7470
|
+
const toolCalls = messageToolCalls(messages[messagePosition]);
|
|
7471
|
+
for (const call of toolCalls) {
|
|
7472
|
+
const callIndex = calls.length;
|
|
7473
|
+
calls.push({ messagePosition, call });
|
|
7474
|
+
addCallKey(callsByEventId, call.eventId || '', callIndex);
|
|
7475
|
+
addCallKey(callsById, normalizedToolId(call.id), callIndex);
|
|
7476
|
+
addCallKey(callsByKind, normalizeToolToken(call.kind || call.name || call.title || ''), callIndex);
|
|
7477
|
+
}
|
|
7478
|
+
}
|
|
7479
|
+
|
|
7480
|
+
const resultRecords = preferredToolResultRecords(messages);
|
|
7481
|
+
const byCallMessage = new Map();
|
|
7482
|
+
const usedCalls = new Set();
|
|
7483
|
+
for (const record of resultRecords.records) {
|
|
7484
|
+
const parentEventId = record.event?.parentEventId || '';
|
|
7485
|
+
let callIndex = firstUnusedIndex(callsByEventId.get(parentEventId), usedCalls);
|
|
7486
|
+
if (callIndex < 0) callIndex = firstUnusedIndex(callsById.get(normalizedToolId(record.result.id)), usedCalls);
|
|
7487
|
+
if (callIndex < 0) callIndex = firstUnusedIndex(callsByKind.get(normalizeToolToken(record.result.name || record.result.kind || record.result.title || '')), usedCalls);
|
|
7488
|
+
if (callIndex < 0) continue;
|
|
7489
|
+
usedCalls.add(callIndex);
|
|
7490
|
+
const messagePosition = calls[callIndex].messagePosition;
|
|
7491
|
+
if (!byCallMessage.has(messagePosition)) byCallMessage.set(messagePosition, []);
|
|
7492
|
+
byCallMessage.get(messagePosition).push(record.index);
|
|
7493
|
+
}
|
|
7494
|
+
return { byCallMessage, duplicateResultIndexes: resultRecords.duplicateResultIndexes };
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
function preferredToolResultRecords(messages) {
|
|
7498
|
+
const records = [];
|
|
7499
|
+
const byId = new Map();
|
|
7500
|
+
const duplicateResultIndexes = new Set();
|
|
7501
|
+
for (let index = 0; index < messages.length; index++) {
|
|
7502
|
+
if (!isPairableToolResultMessage(messages[index])) continue;
|
|
7503
|
+
const result = parseToolResult(messages[index]?.content || '', messages[index]);
|
|
7504
|
+
if (!result) continue;
|
|
7505
|
+
const event = canonicalEventsForMessage(messages[index], 'tool.completed')[0] || null;
|
|
7506
|
+
const record = { index, result, event };
|
|
7507
|
+
records.push(record);
|
|
7508
|
+
const id = normalizedToolId(result.id);
|
|
7509
|
+
if (!id) continue;
|
|
7510
|
+
const existing = byId.get(id);
|
|
7511
|
+
if (!existing) {
|
|
7512
|
+
byId.set(id, record);
|
|
7513
|
+
continue;
|
|
7514
|
+
}
|
|
7515
|
+
const preferred = preferredToolResultRecord(existing, record);
|
|
7516
|
+
const discarded = preferred === existing ? record : existing;
|
|
7517
|
+
duplicateResultIndexes.add(discarded.index);
|
|
7518
|
+
byId.set(id, preferred);
|
|
7519
|
+
}
|
|
7520
|
+
return {
|
|
7521
|
+
records: records.filter((record) => !duplicateResultIndexes.has(record.index)),
|
|
7522
|
+
duplicateResultIndexes
|
|
7523
|
+
};
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
function preferredToolResultRecord(left, right) {
|
|
7527
|
+
return toolResultDisplayScore(right.result) > toolResultDisplayScore(left.result) ? right : left;
|
|
7528
|
+
}
|
|
7529
|
+
|
|
7530
|
+
function toolResultDisplayScore(result) {
|
|
7531
|
+
const rawCategory = normalizeToolToken(result?.rawCategory || '');
|
|
7532
|
+
const kind = normalizeToolToken(result?.kind || '');
|
|
7533
|
+
const category = normalizeToolToken(result?.category || '');
|
|
7534
|
+
let score = 0;
|
|
7535
|
+
if (['exec_command_end', 'patch_apply_end', 'mcp_tool_call_end', 'web_search_end', 'tool_result', 'tool_output'].includes(rawCategory)) score += 2000;
|
|
7536
|
+
if (rawCategory === 'function_call_output' || rawCategory === 'custom_tool_call_output') score -= 1000;
|
|
7537
|
+
if (category && category !== 'function') score += 250;
|
|
7538
|
+
if (kind && !kind.startsWith('call_')) score += 150;
|
|
7539
|
+
if (/^\$\s/.test(String(result?.detail || result?.output || ''))) score += 250;
|
|
7540
|
+
score += Math.min(200, String(result?.output || '').length / 200);
|
|
7541
|
+
return score;
|
|
7542
|
+
}
|
|
7543
|
+
|
|
7544
|
+
function firstUnusedIndex(indexes, used) {
|
|
7545
|
+
if (!Array.isArray(indexes)) return -1;
|
|
7546
|
+
for (const index of indexes) {
|
|
7547
|
+
if (!used.has(index)) return index;
|
|
7548
|
+
}
|
|
7549
|
+
return -1;
|
|
7550
|
+
}
|
|
7551
|
+
|
|
7552
|
+
function isPairableToolResultMessage(message) {
|
|
7553
|
+
return String(message?.role || '').toLowerCase() === 'tool' && Boolean(parseToolResult(message?.content || '', message));
|
|
7554
|
+
}
|
|
7555
|
+
|
|
7171
7556
|
function sessionSummaryText(sessionSummary) {
|
|
7172
7557
|
if (!sessionSummary || typeof sessionSummary !== 'object') return '';
|
|
7173
7558
|
return String(
|
|
@@ -7216,11 +7601,7 @@ function summarySourceLabel(source) {
|
|
|
7216
7601
|
}
|
|
7217
7602
|
|
|
7218
7603
|
function renderTimeGap(previous, current) {
|
|
7219
|
-
|
|
7220
|
-
const prev = new Date(previous).getTime();
|
|
7221
|
-
const next = new Date(current).getTime();
|
|
7222
|
-
if (!Number.isFinite(prev) || !Number.isFinite(next)) return null;
|
|
7223
|
-
const diffSec = (next - prev) / 1000;
|
|
7604
|
+
const diffSec = timeGapSeconds(previous, current);
|
|
7224
7605
|
if (diffSec < 5) return null;
|
|
7225
7606
|
const label = formatGapLabel(diffSec);
|
|
7226
7607
|
if (!label) return null;
|
|
@@ -7230,6 +7611,14 @@ function renderTimeGap(previous, current) {
|
|
|
7230
7611
|
return node;
|
|
7231
7612
|
}
|
|
7232
7613
|
|
|
7614
|
+
function timeGapSeconds(previous, current) {
|
|
7615
|
+
if (!previous || !current) return 0;
|
|
7616
|
+
const prev = new Date(previous).getTime();
|
|
7617
|
+
const next = new Date(current).getTime();
|
|
7618
|
+
if (!Number.isFinite(prev) || !Number.isFinite(next)) return 0;
|
|
7619
|
+
return Math.max(0, (next - prev) / 1000);
|
|
7620
|
+
}
|
|
7621
|
+
|
|
7233
7622
|
function formatGapLabel(diffSec) {
|
|
7234
7623
|
if (diffSec < 60) return Math.round(diffSec) + 's';
|
|
7235
7624
|
if (diffSec < 3600) return Math.round(diffSec / 60) + 'm';
|
|
@@ -7302,12 +7691,13 @@ function highlightSearchMatches(root, term) {
|
|
|
7302
7691
|
walk(root);
|
|
7303
7692
|
}
|
|
7304
7693
|
|
|
7305
|
-
function messageElement(message) {
|
|
7694
|
+
function messageElement(message, options = {}) {
|
|
7695
|
+
if (options.type === 'tool-group') return toolGroupElement(options);
|
|
7306
7696
|
const context = generatedContextForMessage(message);
|
|
7307
7697
|
if (context) return contextMessageElement(message, context);
|
|
7308
7698
|
const role = String(message.role || 'unknown').toLowerCase();
|
|
7309
7699
|
const content = String(message.content || '');
|
|
7310
|
-
const toolCalls = messageToolCalls(message);
|
|
7700
|
+
const toolCalls = messageToolCalls(message, options.pairedToolResults || []);
|
|
7311
7701
|
const toolResult = role === 'tool' ? parseToolResult(content, message) : null;
|
|
7312
7702
|
const contentWithoutTools = toolCalls.length ? stripToolInvocationLines(content) : content;
|
|
7313
7703
|
const toolOnly = toolCalls.length && !contentWithoutTools.trim();
|
|
@@ -7346,7 +7736,22 @@ function messageElement(message) {
|
|
|
7346
7736
|
return row;
|
|
7347
7737
|
}
|
|
7348
7738
|
|
|
7739
|
+
function toolGroupElement(group) {
|
|
7740
|
+
const calls = [];
|
|
7741
|
+
for (const item of group.items || []) {
|
|
7742
|
+
calls.push(...toolCardsForRenderItem(item));
|
|
7743
|
+
}
|
|
7744
|
+
const row = document.createElement('article');
|
|
7745
|
+
row.className = 'message tool tool-call-turn tool-group-turn';
|
|
7746
|
+
const bubble = document.createElement('div');
|
|
7747
|
+
bubble.className = 'bubble';
|
|
7748
|
+
bubble.innerHTML = renderToolGroupCard(calls);
|
|
7749
|
+
row.appendChild(bubble);
|
|
7750
|
+
return row;
|
|
7751
|
+
}
|
|
7752
|
+
|
|
7349
7753
|
function contextMessageElement(message, context) {
|
|
7754
|
+
if (context.kind === 'task_notification') return contextLineElement(message, context);
|
|
7350
7755
|
const row = document.createElement('article');
|
|
7351
7756
|
row.className = 'message context context-' + escClass(context.kind || 'metadata');
|
|
7352
7757
|
const bubble = document.createElement('div');
|
|
@@ -7371,6 +7776,25 @@ function contextMessageElement(message, context) {
|
|
|
7371
7776
|
return row;
|
|
7372
7777
|
}
|
|
7373
7778
|
|
|
7779
|
+
function contextLineElement(message, context) {
|
|
7780
|
+
const row = document.createElement('article');
|
|
7781
|
+
row.className = 'message context context-line-turn context-' + escClass(context.kind || 'metadata');
|
|
7782
|
+
const bubble = document.createElement('div');
|
|
7783
|
+
bubble.className = 'bubble';
|
|
7784
|
+
const line = document.createElement('div');
|
|
7785
|
+
line.className = 'context-line';
|
|
7786
|
+
line.innerHTML =
|
|
7787
|
+
'<span class="context-glyph">' + contextIconSvg(context.kind) + '</span>' +
|
|
7788
|
+
'<span class="context-title">' + esc(contextTitle(context.kind)) + '</span>' +
|
|
7789
|
+
'<span class="context-meta">' + context.chips.map((chip) => '<span class="context-chip">' + esc(chip) + '</span>').join('') + '</span>' +
|
|
7790
|
+
'<span class="context-end"><span class="context-time"' + timeAttr(message.timestamp) + '>' + esc(relativeTime(message.timestamp)) + '</span></span>';
|
|
7791
|
+
const end = line.querySelector('.context-end');
|
|
7792
|
+
if (end) end.appendChild(messageCopyButton(message.content || ''));
|
|
7793
|
+
bubble.appendChild(line);
|
|
7794
|
+
row.appendChild(bubble);
|
|
7795
|
+
return row;
|
|
7796
|
+
}
|
|
7797
|
+
|
|
7374
7798
|
function generatedContextForMessage(message) {
|
|
7375
7799
|
const metadata = message?.metadata || {};
|
|
7376
7800
|
const content = String(message?.content || '').trim();
|
|
@@ -7495,10 +7919,55 @@ function xmlTagText(text, tag) {
|
|
|
7495
7919
|
|
|
7496
7920
|
function renderMessageBodyWithTools(className, content, toolCalls) {
|
|
7497
7921
|
const body = className === 'user' ? renderPlainText(content) : renderRichText(content);
|
|
7498
|
-
const stack = toolCalls.length
|
|
7922
|
+
const stack = toolCalls.length > 1
|
|
7923
|
+
? renderToolGroupCard(toolCalls)
|
|
7924
|
+
: toolCalls.length
|
|
7925
|
+
? '<div class="tool-stack">' + toolCalls.map(renderToolCallout).join('') + '</div>'
|
|
7926
|
+
: '';
|
|
7499
7927
|
return body ? '<div class="tool-body">' + body + '</div>' + stack : stack;
|
|
7500
7928
|
}
|
|
7501
7929
|
|
|
7930
|
+
function renderToolGroupCard(calls) {
|
|
7931
|
+
const tools = Array.isArray(calls) ? calls : [];
|
|
7932
|
+
if (!tools.length) return '';
|
|
7933
|
+
return '<details class="tool-group-card">' +
|
|
7934
|
+
'<summary><span class="tool-group-prefix"><span class="tool-group-caret"></span><span class="tool-glyph">' +
|
|
7935
|
+
renderToolIcon({ category: dominantToolCategory(tools), kind: 'Tool activity' }) +
|
|
7936
|
+
'</span></span><span class="tool-group-title">' +
|
|
7937
|
+
esc(toolStackSummary(tools) || 'Used ' + tools.length + ' tools') +
|
|
7938
|
+
'</span></summary>' +
|
|
7939
|
+
'<div class="tool-stack">' + tools.map(renderToolCallout).join('') + '</div></details>';
|
|
7940
|
+
}
|
|
7941
|
+
|
|
7942
|
+
function toolCardsForRenderItem(item) {
|
|
7943
|
+
const message = item?.message || {};
|
|
7944
|
+
if (String(message.role || '').toLowerCase() === 'tool') {
|
|
7945
|
+
const result = parseToolResult(message.content || '', message);
|
|
7946
|
+
return result ? [toolCardFromResult(result)] : [];
|
|
7947
|
+
}
|
|
7948
|
+
return messageToolCalls(message, item?.pairedToolResults || []);
|
|
7949
|
+
}
|
|
7950
|
+
|
|
7951
|
+
function toolCardFromResult(result) {
|
|
7952
|
+
const category = normalizedToolCategory(result.category, result.kind);
|
|
7953
|
+
const detail = result.detail || result.summary || firstLine(result.output || '') || result.kind || 'Tool output';
|
|
7954
|
+
return toolCard({
|
|
7955
|
+
kind: result.name || result.kind || 'Tool output',
|
|
7956
|
+
title: result.kind || result.title || '',
|
|
7957
|
+
category,
|
|
7958
|
+
categoryLabel: result.categoryLabel || toolCategoryLabel(category),
|
|
7959
|
+
icon: result.icon || toolIcon(category, result.kind || ''),
|
|
7960
|
+
status: result.status || 'completed',
|
|
7961
|
+
argument: detail,
|
|
7962
|
+
rawInputSummary: detail,
|
|
7963
|
+
inputPreview: '',
|
|
7964
|
+
target: result.target || '',
|
|
7965
|
+
id: result.id || '',
|
|
7966
|
+
result,
|
|
7967
|
+
resultOnly: true
|
|
7968
|
+
});
|
|
7969
|
+
}
|
|
7970
|
+
|
|
7502
7971
|
function copyIconSvg() {
|
|
7503
7972
|
return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>';
|
|
7504
7973
|
}
|
|
@@ -7547,13 +8016,13 @@ function detectToolName(text) {
|
|
|
7547
8016
|
return tool ? tool.label : '';
|
|
7548
8017
|
}
|
|
7549
8018
|
|
|
7550
|
-
function messageToolCalls(message) {
|
|
8019
|
+
function messageToolCalls(message, pairedResultMessages) {
|
|
7551
8020
|
const eventCalls = canonicalEventsForMessage(message, 'tool.called').map(toolCallFromEvent).filter(Boolean);
|
|
7552
|
-
if (eventCalls.length) return eventCalls;
|
|
8021
|
+
if (eventCalls.length) return attachPairedToolResults(eventCalls, pairedResultMessages);
|
|
7553
8022
|
const metadataCalls = Array.isArray(message?.metadata?.toolCalls) ? message.metadata.toolCalls : [];
|
|
7554
8023
|
const textCalls = toolInvocationsFromText(message?.content || '');
|
|
7555
8024
|
if (metadataCalls.length) {
|
|
7556
|
-
|
|
8025
|
+
const calls = metadataCalls.map((meta, index) => {
|
|
7557
8026
|
const text = textCalls[index] || {};
|
|
7558
8027
|
const kind = meta.displayName || meta.name || text.kind || 'Tool';
|
|
7559
8028
|
const argument = meta.argument || meta.rawInputSummary || text.argument || '';
|
|
@@ -7569,10 +8038,11 @@ function messageToolCalls(message) {
|
|
|
7569
8038
|
categoryLabel: meta.categoryLabel || '',
|
|
7570
8039
|
icon: meta.icon || '',
|
|
7571
8040
|
rawCategory: meta.rawCategory || '',
|
|
7572
|
-
id: meta.id || '',
|
|
8041
|
+
id: meta.id || meta.callId || meta.call_id || meta.toolCallId || meta.tool_call_id || meta.toolUseId || meta.tool_use_id || '',
|
|
7573
8042
|
arguments: meta.arguments || null
|
|
7574
8043
|
});
|
|
7575
8044
|
}).filter((call) => call.kind || call.title || call.argument);
|
|
8045
|
+
return attachPairedToolResults(calls, pairedResultMessages);
|
|
7576
8046
|
}
|
|
7577
8047
|
const total = Math.max(metadataCalls.length, textCalls.length);
|
|
7578
8048
|
const calls = [];
|
|
@@ -7592,11 +8062,67 @@ function messageToolCalls(message) {
|
|
|
7592
8062
|
categoryLabel: meta.categoryLabel || '',
|
|
7593
8063
|
icon: meta.icon || '',
|
|
7594
8064
|
rawCategory: meta.rawCategory || '',
|
|
7595
|
-
id: meta.id || '',
|
|
8065
|
+
id: meta.id || meta.callId || meta.call_id || meta.toolCallId || meta.tool_call_id || meta.toolUseId || meta.tool_use_id || '',
|
|
7596
8066
|
arguments: meta.arguments || null
|
|
7597
8067
|
}));
|
|
7598
8068
|
}
|
|
7599
|
-
return calls.filter((call) => call.kind || call.title || call.argument);
|
|
8069
|
+
return attachPairedToolResults(calls.filter((call) => call.kind || call.title || call.argument), pairedResultMessages);
|
|
8070
|
+
}
|
|
8071
|
+
|
|
8072
|
+
function attachPairedToolResults(calls, pairedResultMessages) {
|
|
8073
|
+
if (!Array.isArray(pairedResultMessages) || !pairedResultMessages.length) return calls;
|
|
8074
|
+
const results = pairedResultMessages
|
|
8075
|
+
.map((message) => parseToolResult(message?.content || '', message))
|
|
8076
|
+
.filter(Boolean);
|
|
8077
|
+
if (!results.length) return calls;
|
|
8078
|
+
const remaining = results.slice();
|
|
8079
|
+
return calls.map((call) => {
|
|
8080
|
+
const matchIndex = pairedToolResultIndex(call, remaining);
|
|
8081
|
+
if (matchIndex < 0) return call;
|
|
8082
|
+
const [result] = remaining.splice(matchIndex, 1);
|
|
8083
|
+
return { ...call, result };
|
|
8084
|
+
});
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
function pairedToolResultIndex(call, results) {
|
|
8088
|
+
const callId = normalizedToolId(call?.id);
|
|
8089
|
+
if (callId) {
|
|
8090
|
+
const idMatch = results.findIndex((result) => normalizedToolId(result?.id) === callId);
|
|
8091
|
+
if (idMatch >= 0) return idMatch;
|
|
8092
|
+
}
|
|
8093
|
+
const callKind = normalizeToolToken(call?.kind || call?.name || call?.title || '');
|
|
8094
|
+
if (callKind) {
|
|
8095
|
+
const kindMatch = results.findIndex((result) => normalizeToolToken(result?.name || result?.kind || result?.title || '') === callKind);
|
|
8096
|
+
if (kindMatch >= 0) return kindMatch;
|
|
8097
|
+
}
|
|
8098
|
+
return results.length ? 0 : -1;
|
|
8099
|
+
}
|
|
8100
|
+
|
|
8101
|
+
function toolStackSummary(tools) {
|
|
8102
|
+
const counts = {};
|
|
8103
|
+
for (const tool of tools) {
|
|
8104
|
+
const category = normalizedToolCategory(tool.category, tool.kind);
|
|
8105
|
+
counts[category] = (counts[category] || 0) + 1;
|
|
8106
|
+
}
|
|
8107
|
+
const parts = [];
|
|
8108
|
+
if (counts.read) parts.push('explored ' + counts.read + ' ' + (counts.read === 1 ? 'file' : 'files'));
|
|
8109
|
+
if (counts.search) parts.push('searched ' + counts.search + ' ' + (counts.search === 1 ? 'time' : 'times'));
|
|
8110
|
+
if (counts.shell) parts.push('ran ' + counts.shell + ' ' + (counts.shell === 1 ? 'command' : 'commands'));
|
|
8111
|
+
if (counts.edit) parts.push('edited ' + counts.edit + ' ' + (counts.edit === 1 ? 'file' : 'files'));
|
|
8112
|
+
const known = ['read', 'search', 'shell', 'edit'];
|
|
8113
|
+
const other = Object.entries(counts).filter(([key]) => !known.includes(key)).reduce((sum, [, count]) => sum + count, 0);
|
|
8114
|
+
if (other) parts.push('used ' + other + ' ' + (other === 1 ? 'tool' : 'tools'));
|
|
8115
|
+
const text = parts.join(', ');
|
|
8116
|
+
return text ? text.charAt(0).toUpperCase() + text.slice(1) : '';
|
|
8117
|
+
}
|
|
8118
|
+
|
|
8119
|
+
function dominantToolCategory(tools) {
|
|
8120
|
+
const counts = {};
|
|
8121
|
+
for (const tool of tools || []) {
|
|
8122
|
+
const category = normalizedToolCategory(tool.category, tool.kind);
|
|
8123
|
+
counts[category] = (counts[category] || 0) + 1;
|
|
8124
|
+
}
|
|
8125
|
+
return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'tool';
|
|
7600
8126
|
}
|
|
7601
8127
|
|
|
7602
8128
|
function toolCallFromEvent(event) {
|
|
@@ -7615,7 +8141,8 @@ function toolCallFromEvent(event) {
|
|
|
7615
8141
|
categoryLabel: meta.categoryLabel || '',
|
|
7616
8142
|
icon: meta.icon || event.indexed?.toolIcon || '',
|
|
7617
8143
|
rawCategory: meta.rawCategory || '',
|
|
7618
|
-
id: meta.id || '',
|
|
8144
|
+
id: meta.id || meta.callId || meta.call_id || meta.toolCallId || meta.tool_call_id || meta.toolUseId || meta.tool_use_id || '',
|
|
8145
|
+
eventId: event.eventId || '',
|
|
7619
8146
|
arguments: meta.arguments || null
|
|
7620
8147
|
});
|
|
7621
8148
|
}
|
|
@@ -7682,6 +8209,10 @@ function normalizeToolToken(value) {
|
|
|
7682
8209
|
return String(value || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
7683
8210
|
}
|
|
7684
8211
|
|
|
8212
|
+
function normalizedToolId(value) {
|
|
8213
|
+
return String(value || '').trim().toLowerCase();
|
|
8214
|
+
}
|
|
8215
|
+
|
|
7685
8216
|
function toolCategoryLabel(category) {
|
|
7686
8217
|
const labels = {
|
|
7687
8218
|
shell: 'Shell',
|
|
@@ -7710,18 +8241,92 @@ function escClass(value) {
|
|
|
7710
8241
|
function renderToolCallout(tool) {
|
|
7711
8242
|
const card = toolCard(tool);
|
|
7712
8243
|
const isSkill = card.category === 'skill' || card.kind === 'Skill';
|
|
7713
|
-
const title = card.title || (isSkill ? 'Skill loaded' : card.kind);
|
|
7714
|
-
const category = card.category ? '<span class="tool-chip">' + esc(card.categoryLabel || card.category) + '</span>' : '';
|
|
7715
|
-
const statusClass = humanToolStatusClass(card.status);
|
|
7716
|
-
const status = card.status ? '<span class="tool-status' + (statusClass ? ' ' + statusClass : '') + '">' + esc(humanToolStatus(card.status)) + '</span>' : '';
|
|
7717
|
-
const target = card.target ? '<span class="tool-target">' + esc(card.target) + '</span>' : '';
|
|
7718
8244
|
const diff = renderToolDiff(card);
|
|
7719
|
-
const
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
'
|
|
7724
|
-
|
|
8245
|
+
const category = normalizedToolCategory(card.category, card.kind);
|
|
8246
|
+
return '<details class="tool-callout ' + escClass(category) + (isSkill ? ' skill' : '') + (card.result ? ' has-result' : '') + '" data-category="' + esc(escClass(category)) + '">' +
|
|
8247
|
+
renderToolActivitySummary(card) +
|
|
8248
|
+
renderToolCalloutBody(card, diff) +
|
|
8249
|
+
'</details>';
|
|
8250
|
+
}
|
|
8251
|
+
|
|
8252
|
+
function renderToolActivitySummary(card) {
|
|
8253
|
+
const label = toolActivityLabel(card);
|
|
8254
|
+
const count = card.result?.count || '';
|
|
8255
|
+
const statusClass = humanToolStatusClass(card.status);
|
|
8256
|
+
const status = !card.result && card.status ? '<span class="tool-status' + (statusClass ? ' ' + statusClass : '') + '">' + esc(humanToolStatus(card.status)) + '</span>' : '';
|
|
8257
|
+
return '<summary><span class="tool-glyph">' + renderToolIcon(card) + '</span>' +
|
|
8258
|
+
'<span class="tool-call-line"><span class="tool-action">' + esc(label.action) + '</span>' +
|
|
8259
|
+
(label.subject ? '<span class="tool-subject">' + esc(label.subject) + '</span>' : '') +
|
|
8260
|
+
'</span>' + status +
|
|
8261
|
+
(count ? '<span class="tool-result-count">' + esc(count) + '</span>' : '') +
|
|
8262
|
+
'</summary>';
|
|
8263
|
+
}
|
|
8264
|
+
|
|
8265
|
+
function renderToolCalloutBody(card, diff) {
|
|
8266
|
+
const rows = [];
|
|
8267
|
+
const meta = [];
|
|
8268
|
+
if (card.categoryLabel) meta.push(card.categoryLabel);
|
|
8269
|
+
if (card.status) meta.push(humanToolStatus(card.status));
|
|
8270
|
+
if (card.target) meta.push(card.target);
|
|
8271
|
+
if (meta.length) rows.push('<div class="tool-call-meta">' + meta.map((item) => '<span class="tool-chip">' + esc(item) + '</span>').join('') + '</div>');
|
|
8272
|
+
const preview = card.resultOnly ? '' : String(card.inputPreview || card.argument || '').trim();
|
|
8273
|
+
if (preview) rows.push('<pre class="tool-preview">' + esc(preview) + '</pre>');
|
|
8274
|
+
if (diff) rows.push(diff);
|
|
8275
|
+
if (card.result) rows.push(renderPairedToolResult(card.result));
|
|
8276
|
+
return rows.length ? '<div class="tool-callout-body">' + rows.join('') + '</div>' : '';
|
|
8277
|
+
}
|
|
8278
|
+
|
|
8279
|
+
function renderPairedToolResult(result) {
|
|
8280
|
+
const category = normalizedToolCategory(result.category, result.kind);
|
|
8281
|
+
const meta = [result.kind, result.detail, result.count].filter(Boolean);
|
|
8282
|
+
return '<div class="tool-paired-result" data-category="' + esc(escClass(category)) + '">' +
|
|
8283
|
+
(meta.length ? '<div class="tool-result-meta">' + meta.map((item) => '<span>' + esc(item) + '</span>').join('') + '</div>' : '') +
|
|
8284
|
+
renderToolOutput(result.output || '', { lineStart: result.lineStart }) + '</div>';
|
|
8285
|
+
}
|
|
8286
|
+
|
|
8287
|
+
function toolActivityLabel(card) {
|
|
8288
|
+
const category = normalizedToolCategory(card.category, card.kind);
|
|
8289
|
+
const fallback = toolSubjectFallback(card);
|
|
8290
|
+
if (category === 'shell') return { action: 'Ran', subject: shellCommandText(card) || fallback };
|
|
8291
|
+
if (category === 'read') return { action: 'Read', subject: compactPathLabel(card.target || fallback) };
|
|
8292
|
+
if (category === 'search') return { action: 'Searched', subject: fallback };
|
|
8293
|
+
if (category === 'edit') return { action: 'Edited', subject: compactPathLabel(card.target || fallback) };
|
|
8294
|
+
if (category === 'web') return { action: webToolVerb(card), subject: fallback };
|
|
8295
|
+
if (category === 'task') return { action: 'Ran', subject: fallback };
|
|
8296
|
+
if (category === 'skill') return { action: 'Loaded', subject: fallback };
|
|
8297
|
+
if (category === 'mcp') return { action: 'Called', subject: fallback };
|
|
8298
|
+
return { action: card.title || card.kind || 'Used', subject: fallback && fallback !== (card.title || card.kind) ? fallback : '' };
|
|
8299
|
+
}
|
|
8300
|
+
|
|
8301
|
+
function shellCommandText(card) {
|
|
8302
|
+
const args = card.arguments && typeof card.arguments === 'object' && !Array.isArray(card.arguments) ? card.arguments : {};
|
|
8303
|
+
return firstToolString(args.cmd, args.command, args.script, card.inputPreview, card.argument);
|
|
8304
|
+
}
|
|
8305
|
+
|
|
8306
|
+
function webToolVerb(card) {
|
|
8307
|
+
const key = normalizeToolToken(card.kind || card.title || '');
|
|
8308
|
+
if (key.includes('fetch')) return 'Fetched';
|
|
8309
|
+
if (key.includes('search')) return 'Searched';
|
|
8310
|
+
if (key.includes('open')) return 'Opened';
|
|
8311
|
+
return 'Opened';
|
|
8312
|
+
}
|
|
8313
|
+
|
|
8314
|
+
function toolSubjectFallback(card) {
|
|
8315
|
+
return firstToolString(card.target, card.inputPreview, card.argument, card.title, card.kind);
|
|
8316
|
+
}
|
|
8317
|
+
|
|
8318
|
+
function compactPathLabel(value) {
|
|
8319
|
+
const text = String(value || '').trim();
|
|
8320
|
+
if (!text || /\s/.test(text) || !text.includes('/')) return text;
|
|
8321
|
+
return text.split('/').filter(Boolean).pop() || text;
|
|
8322
|
+
}
|
|
8323
|
+
|
|
8324
|
+
function firstToolString() {
|
|
8325
|
+
for (const value of arguments) {
|
|
8326
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
8327
|
+
if (Array.isArray(value) && value.length) return value.map((item) => String(item || '').trim()).filter(Boolean).join(' ');
|
|
8328
|
+
}
|
|
8329
|
+
return '';
|
|
7725
8330
|
}
|
|
7726
8331
|
|
|
7727
8332
|
function hasPatchMarker(value) {
|
|
@@ -7818,7 +8423,7 @@ function renderToolDiff(card) {
|
|
|
7818
8423
|
if (adds) summaryParts.push('<span class="add-count">+' + adds + '</span>');
|
|
7819
8424
|
if (dels) summaryParts.push('<span class="del-count">-' + dels + '</span>');
|
|
7820
8425
|
const summaryLabel = editCount > 1 ? editCount + ' edits' : label;
|
|
7821
|
-
return '<details class="tool-diff"
|
|
8426
|
+
return '<details class="tool-diff">' +
|
|
7822
8427
|
'<summary class="tool-diff-summary">' + esc(summaryLabel) + summaryParts.join('') + '</summary>' +
|
|
7823
8428
|
'<div class="tool-diff-body">' + blocks.join('') + '</div>' +
|
|
7824
8429
|
'</details>';
|
|
@@ -7966,6 +8571,9 @@ function toolResultFromMetadata(result, indexed, fallbackText) {
|
|
|
7966
8571
|
if (!output && !result.summary && !indexed.summary) return null;
|
|
7967
8572
|
const category = normalizedToolCategory(result.category || indexed.toolCategory || result.rawCategory || '', result.kind || indexed.title || '');
|
|
7968
8573
|
return {
|
|
8574
|
+
id: result.id || result.callId || result.call_id || result.toolCallId || result.tool_call_id || result.toolUseId || result.tool_use_id || '',
|
|
8575
|
+
name: result.name || result.toolName || indexed.toolName || '',
|
|
8576
|
+
rawCategory: result.rawCategory || '',
|
|
7969
8577
|
header: 'Tool result' + (result.title || indexed.title ? ' · ' + (result.title || indexed.title) : ''),
|
|
7970
8578
|
kind: result.kind || indexed.title || 'Tool output',
|
|
7971
8579
|
category,
|
|
@@ -7974,6 +8582,7 @@ function toolResultFromMetadata(result, indexed, fallbackText) {
|
|
|
7974
8582
|
detail: result.summary || indexed.summary || firstLine(output),
|
|
7975
8583
|
count: result.lineCount ? result.lineCount + ' line' + (Number(result.lineCount) === 1 ? '' : 's') : lineCountLabel(output || result.summary || indexed.summary || ''),
|
|
7976
8584
|
output: output || result.summary || indexed.summary || '',
|
|
8585
|
+
lineStart: Number(result.startLine || result.start_line || result.lineStart || result.line_start || 0) || 0,
|
|
7977
8586
|
collapsed: Boolean(result.collapsed) || String(output).split('\\n').length > 18
|
|
7978
8587
|
};
|
|
7979
8588
|
}
|
|
@@ -7993,6 +8602,7 @@ function parseFileViewResult(text) {
|
|
|
7993
8602
|
detail: [attrs.path || basename, lineLabel].filter(Boolean).join(' · '),
|
|
7994
8603
|
count: lineCountLabel(code),
|
|
7995
8604
|
output: code,
|
|
8605
|
+
lineStart: Number(attrs.start_line || 0) || 0,
|
|
7996
8606
|
collapsed: code.split('\\n').length > 24
|
|
7997
8607
|
};
|
|
7998
8608
|
}
|
|
@@ -8050,14 +8660,25 @@ function genericToolResult(text) {
|
|
|
8050
8660
|
};
|
|
8051
8661
|
}
|
|
8052
8662
|
|
|
8053
|
-
function renderToolResult(result) {
|
|
8054
|
-
const
|
|
8663
|
+
function renderToolResult(result, options) {
|
|
8664
|
+
const inline = Boolean(options && options.inline);
|
|
8055
8665
|
const category = normalizedToolCategory(result.category, result.kind);
|
|
8056
|
-
return '<details class="tool-result" data-category="' + esc(escClass(category)) + '"
|
|
8666
|
+
return '<details class="tool-result' + (inline ? ' inline' : '') + '" data-category="' + esc(escClass(category)) + '">' +
|
|
8057
8667
|
'<summary><span class="tool-result-kind"><span class="tool-glyph">' + renderToolIcon({ icon: result.icon, category, kind: result.kind }) + '</span>' + esc(result.kind) + '</span>' +
|
|
8058
8668
|
(result.detail ? '<span class="tool-result-detail">' + esc(result.detail) + '</span>' : '') +
|
|
8059
8669
|
(result.count ? '<span class="tool-result-count">' + esc(result.count) + '</span>' : '') +
|
|
8060
|
-
'</summary
|
|
8670
|
+
'</summary>' + renderToolOutput(result.output || '', { lineStart: result.lineStart }) + '</details>';
|
|
8671
|
+
}
|
|
8672
|
+
|
|
8673
|
+
function renderToolOutput(output, options = {}) {
|
|
8674
|
+
const text = String(output || '');
|
|
8675
|
+
const lines = text.split('\\n');
|
|
8676
|
+
if (lines.length <= 1) return '<pre class="tool-output">' + esc(text) + '</pre>';
|
|
8677
|
+
const start = Number(options.lineStart || 0) || 1;
|
|
8678
|
+
return '<div class="tool-output tool-output-lines">' + lines.map((line, index) =>
|
|
8679
|
+
'<div class="tool-output-line"><span class="tool-line-number">' + esc(start + index) + '</span><span class="tool-line-text">' +
|
|
8680
|
+
(line ? esc(line) : ' ') + '</span></div>'
|
|
8681
|
+
).join('') + '</div>';
|
|
8061
8682
|
}
|
|
8062
8683
|
|
|
8063
8684
|
function parseXmlishAttrs(value) {
|
|
@@ -8710,7 +9331,7 @@ setupStatsActivityControls();
|
|
|
8710
9331
|
setupStatsBreakdownControls();
|
|
8711
9332
|
|
|
8712
9333
|
async function loadStats() {
|
|
8713
|
-
const elements = ['chartTokensPerDay', 'chartSessionsPerDay', 'chartTokensPerRepo', 'chartSessionsPerRepo', 'statsAgentHeatmap', 'statsChatHeatmap'].map((id) => document.getElementById(id));
|
|
9334
|
+
const elements = ['chartTokensPerDay', 'chartSessionsPerDay', 'chartTokensPerRepo', 'chartSessionsPerRepo', 'statsAgentHeatmap', 'statsChatHeatmap', 'statsSdkHeatmap'].map((id) => document.getElementById(id));
|
|
8714
9335
|
for (const el of elements) {
|
|
8715
9336
|
if (el) {
|
|
8716
9337
|
el.classList.add('stats-empty');
|
|
@@ -9003,8 +9624,9 @@ function renderStatsDailyCharts() {
|
|
|
9003
9624
|
if (empty2) { empty2.classList.add('stats-empty'); empty2.textContent = 'No days in this range.'; }
|
|
9004
9625
|
return;
|
|
9005
9626
|
}
|
|
9006
|
-
|
|
9007
|
-
renderDailyChart('
|
|
9627
|
+
const canonicalGroups = statsCanonicalOrderedGroups(groups);
|
|
9628
|
+
renderDailyChart('chartTokensPerDay', densified, canonicalGroups, usageMetric, 336);
|
|
9629
|
+
renderDailyChart('chartSessionsPerDay', densified, canonicalGroups, activityMetric, 336);
|
|
9008
9630
|
}
|
|
9009
9631
|
|
|
9010
9632
|
function renderStats(payload) {
|
|
@@ -9019,8 +9641,9 @@ function renderStats(payload) {
|
|
|
9019
9641
|
renderStatsLegend(groups, breakdownTotals);
|
|
9020
9642
|
renderStatsDailyCharts();
|
|
9021
9643
|
const repoRows = statsRepoRowsForRange(payload);
|
|
9022
|
-
|
|
9023
|
-
renderRepoChart('
|
|
9644
|
+
const canonicalGroupsForRepo = statsCanonicalOrderedGroups(groups);
|
|
9645
|
+
renderRepoChart('chartTokensPerRepo', repoRows, canonicalGroupsForRepo, statsTokenMetric());
|
|
9646
|
+
renderRepoChart('chartSessionsPerRepo', repoRows, canonicalGroupsForRepo, statsActivityMetric());
|
|
9024
9647
|
}
|
|
9025
9648
|
|
|
9026
9649
|
function statsTokenMetric() {
|
|
@@ -9392,6 +10015,10 @@ function renderStatsMetrics(payload) {
|
|
|
9392
10015
|
const totalOutTok = Number(payload.total_output_tokens || 0);
|
|
9393
10016
|
const totalCacheTok = Number(payload.total_cache_tokens || 0);
|
|
9394
10017
|
const totalEstimatedTok = Number(payload.total_estimated_tokens || 0);
|
|
10018
|
+
const sdkSessions = Number(payload.sdk_session_count || 0);
|
|
10019
|
+
const sdkTokens = Number(payload.sdk_total_tokens || 0);
|
|
10020
|
+
const sdkInputTokens = Number(payload.sdk_total_input_tokens || 0);
|
|
10021
|
+
const sdkOutputTokens = Number(payload.sdk_total_output_tokens || 0);
|
|
9395
10022
|
const um = Number(payload.user_message_count || 0);
|
|
9396
10023
|
const tm = Number(payload.message_count || 0);
|
|
9397
10024
|
const avgMsgs = Number(payload.avg_messages_per_conversation);
|
|
@@ -9416,6 +10043,13 @@ function renderStatsMetrics(payload) {
|
|
|
9416
10043
|
value: formatFullNumber(payload.session_count || 0),
|
|
9417
10044
|
sub: formatFullNumber(payload.agent_session_count || 0) + ' agent · ' + formatFullNumber(payload.chat_session_count || 0) + ' chat'
|
|
9418
10045
|
},
|
|
10046
|
+
sdkSessions || sdkTokens ? {
|
|
10047
|
+
label: 'SDK jobs',
|
|
10048
|
+
value: formatFullNumber(sdkSessions),
|
|
10049
|
+
sub: (sdkTokens ? formatCompactNumber(sdkTokens) + ' tokens' : '0 tokens') +
|
|
10050
|
+
(sdkInputTokens || sdkOutputTokens ? ' · ' + formatCompactNumber(sdkInputTokens) + ' in / ' + formatCompactNumber(sdkOutputTokens) + ' out' : '') +
|
|
10051
|
+
' · kept separate'
|
|
10052
|
+
} : null,
|
|
9419
10053
|
{
|
|
9420
10054
|
label: 'Messages',
|
|
9421
10055
|
value: formatFullNumber(tm),
|
|
@@ -9472,6 +10106,7 @@ function renderStatsMetrics(payload) {
|
|
|
9472
10106
|
}
|
|
9473
10107
|
];
|
|
9474
10108
|
container.innerHTML = metrics
|
|
10109
|
+
.filter(Boolean)
|
|
9475
10110
|
.map(function (metric) {
|
|
9476
10111
|
const vt =
|
|
9477
10112
|
metric.valueHtml == null &&
|
|
@@ -9545,7 +10180,11 @@ function renderHeatmapSection(payload) {
|
|
|
9545
10180
|
renderSecondaryHeatmap(payload.split_stats && payload.split_stats.agent, 'statsAgentActivitySub', 'statsAgentHeatmap', range, metric);
|
|
9546
10181
|
renderSecondaryHeatmap(payload.split_stats && payload.split_stats.chat, 'statsChatActivitySub', 'statsChatHeatmap', range, metric, {
|
|
9547
10182
|
emptySubText: '',
|
|
9548
|
-
emptyText: 'No chat activity yet.
|
|
10183
|
+
emptyText: 'No chat activity yet. Run agentlog import chatgpt or agentlog import claude-web for official export instructions.'
|
|
10184
|
+
});
|
|
10185
|
+
renderSecondaryHeatmap(payload.split_stats && payload.split_stats.sdk, 'statsSdkActivitySub', 'statsSdkHeatmap', range, metric, {
|
|
10186
|
+
emptySubText: '',
|
|
10187
|
+
emptyText: 'No SDK jobs imported.'
|
|
9549
10188
|
});
|
|
9550
10189
|
}
|
|
9551
10190
|
|
|
@@ -9780,15 +10419,11 @@ function renderDailyChart(elementId, daily, providers, metric, height) {
|
|
|
9780
10419
|
return '<line class="stats-axis-line" x1="' + padding.left + '" x2="' + (padding.left + innerW) + '" y1="' + y.toFixed(2) + '" y2="' + y.toFixed(2) + '"/>'
|
|
9781
10420
|
+ '<text class="stats-axis-label" x="' + (padding.left - 6) + '" y="' + (y + 3).toFixed(2) + '" text-anchor="end">' + esc(formatCompactNumber(value)) + '</text>';
|
|
9782
10421
|
}).join('');
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
const cx = padding.left + idx * xStep + xStep / 2;
|
|
9789
|
-
const tick = formatChartMonthTick(day.date);
|
|
9790
|
-
return '<text class="stats-axis-label" x="' + cx.toFixed(2) + '" y="' + (padding.top + innerH + 18) + '" text-anchor="middle">' + esc(tick) + '</text>';
|
|
9791
|
-
}).join('');
|
|
10422
|
+
const xLabels = dailyChartMonthTicks(visibleDaily, xStep, padding.left, innerW)
|
|
10423
|
+
.map((tickEntry) =>
|
|
10424
|
+
'<text class="stats-axis-label" x="' + tickEntry.x.toFixed(2) + '" y="' + (padding.top + innerH + 18) + '" text-anchor="middle">' + esc(tickEntry.label) + '</text>'
|
|
10425
|
+
)
|
|
10426
|
+
.join('');
|
|
9792
10427
|
const hits = visibleDaily.map((day, dayIdx) => {
|
|
9793
10428
|
const xHit = padding.left + dayIdx * xStep;
|
|
9794
10429
|
return '<rect class="stats-chart-hit" pointer-events="all" x="' + xHit.toFixed(2) + '" y="' + padding.top + '" width="' + xStep.toFixed(2) + '" height="' + innerH.toFixed(2) + '" fill="transparent" data-day-index="' + dayIdx + '"/>';
|
|
@@ -9800,6 +10435,33 @@ function renderDailyChart(elementId, daily, providers, metric, height) {
|
|
|
9800
10435
|
bindDailyChartHover(el);
|
|
9801
10436
|
}
|
|
9802
10437
|
|
|
10438
|
+
function dailyChartMonthTicks(daily, xStep, left, innerW) {
|
|
10439
|
+
const monthStarts = [];
|
|
10440
|
+
let prevYm = '';
|
|
10441
|
+
for (let idx = 0; idx < daily.length; idx += 1) {
|
|
10442
|
+
const day = daily[idx];
|
|
10443
|
+
const ym = String(day?.date || '').slice(0, 7);
|
|
10444
|
+
if (!/^\\d{4}-\\d{2}$/.test(ym) || ym === prevYm) continue;
|
|
10445
|
+
prevYm = ym;
|
|
10446
|
+
monthStarts.push({ idx, date: day.date, ym });
|
|
10447
|
+
}
|
|
10448
|
+
const maxLabels = Math.max(2, Math.floor(innerW / 58));
|
|
10449
|
+
const step = Math.max(1, Math.ceil(monthStarts.length / maxLabels));
|
|
10450
|
+
const selected = [];
|
|
10451
|
+
for (let idx = 0; idx < monthStarts.length; idx += step) selected.push(monthStarts[idx]);
|
|
10452
|
+
const last = monthStarts[monthStarts.length - 1];
|
|
10453
|
+
if (last && selected[selected.length - 1] !== last) {
|
|
10454
|
+
const previous = selected[selected.length - 1];
|
|
10455
|
+
if (previous && monthStarts.indexOf(previous) >= monthStarts.length - step) selected[selected.length - 1] = last;
|
|
10456
|
+
else selected.push(last);
|
|
10457
|
+
}
|
|
10458
|
+
return selected
|
|
10459
|
+
.map((entry) => ({
|
|
10460
|
+
x: left + entry.idx * xStep + xStep / 2,
|
|
10461
|
+
label: formatChartMonthTick(entry.date)
|
|
10462
|
+
}));
|
|
10463
|
+
}
|
|
10464
|
+
|
|
9803
10465
|
function trimEmptyDailyChartEdges(daily, providers, metric) {
|
|
9804
10466
|
const rows = Array.isArray(daily) ? daily : [];
|
|
9805
10467
|
const hasValue = (day) => providers.some((provider) => statsMetricValue(day?.providers?.[provider], metric) > 0);
|
|
@@ -10246,8 +10908,8 @@ Start here:
|
|
|
10246
10908
|
Archive and import:
|
|
10247
10909
|
init interactive setup and optional first import
|
|
10248
10910
|
import import local Codex, Claude, Gemini, Devin, Cursor, Cline, OpenCode, Aider, and Antigravity history
|
|
10249
|
-
import chatgpt
|
|
10250
|
-
import claude-web
|
|
10911
|
+
import chatgpt [path] show ChatGPT export instructions; with path, import a ZIP/folder
|
|
10912
|
+
import claude-web [path] show Claude.ai export instructions; with path, import a ZIP/folder
|
|
10251
10913
|
import windsurf <path> import downloaded Windsurf trajectory Markdown file/folder
|
|
10252
10914
|
import accounts list or rename ChatGPT/Claude.ai export accounts
|
|
10253
10915
|
sync choose a remote, preview, confirm, then upload archive objects
|
|
@@ -10303,8 +10965,8 @@ agentlog import
|
|
|
10303
10965
|
Usage:
|
|
10304
10966
|
agentlog import --source <source> [--since 30d|all]
|
|
10305
10967
|
agentlog import --sources <a,b,c> [--since 30d|all]
|
|
10306
|
-
agentlog import chatgpt
|
|
10307
|
-
agentlog import claude-web
|
|
10968
|
+
agentlog import chatgpt [path] [--username <name>] [--scope local|team]
|
|
10969
|
+
agentlog import claude-web [path] [--username <name>] [--scope local|team]
|
|
10308
10970
|
agentlog import windsurf <file-or-folder>
|
|
10309
10971
|
agentlog import accounts list
|
|
10310
10972
|
agentlog import accounts rename <provider> <account-id-or-username> --display-name <name>
|
|
@@ -10312,6 +10974,7 @@ Usage:
|
|
|
10312
10974
|
Import sources:
|
|
10313
10975
|
codex-cli terminal Codex sessions from Codex state and rollout files
|
|
10314
10976
|
codex-desktop Codex desktop app sessions from Codex state and rollout files
|
|
10977
|
+
codex-sdk high-volume Codex exec/SDK batch jobs; opt-in
|
|
10315
10978
|
claude interactive Claude Code CLI JSONL transcripts
|
|
10316
10979
|
claude-code-desktop Claude Code sessions launched from the Claude desktop app
|
|
10317
10980
|
claude-workspace Claude app workspace/local-agent sessions
|
|
@@ -10329,13 +10992,14 @@ Import sources:
|
|
|
10329
10992
|
all configured default local sources
|
|
10330
10993
|
|
|
10331
10994
|
Web export sources:
|
|
10332
|
-
chatgpt
|
|
10333
|
-
claude-web
|
|
10995
|
+
chatgpt instructions for requesting a ChatGPT export; imports downloaded ZIP/folder
|
|
10996
|
+
claude-web instructions for requesting a Claude.ai export; imports downloaded ZIP/folder
|
|
10334
10997
|
windsurf downloaded Cascade trajectory Markdown file/folder
|
|
10335
10998
|
|
|
10336
10999
|
Examples:
|
|
10337
11000
|
agentlog import --source codex-cli --since 30d
|
|
10338
11001
|
agentlog import --source codex-desktop --since all
|
|
11002
|
+
agentlog import --source codex-sdk --since all
|
|
10339
11003
|
agentlog import --source claude --since 30d
|
|
10340
11004
|
agentlog import --source claude-code-desktop --since all
|
|
10341
11005
|
agentlog import --source claude-workspace --since all
|
|
@@ -10351,6 +11015,8 @@ Examples:
|
|
|
10351
11015
|
agentlog import --source cursor --since all --explain-skips
|
|
10352
11016
|
agentlog import --source cursor --since all --explain-skips --json
|
|
10353
11017
|
agentlog import status --json
|
|
11018
|
+
agentlog import chatgpt
|
|
11019
|
+
agentlog import claude-web
|
|
10354
11020
|
agentlog import chatgpt ~/Downloads/chatgpt-export.zip --username you@example.com
|
|
10355
11021
|
agentlog import claude-web ~/Downloads/claude-export --username brian --display-name Brian --scope local
|
|
10356
11022
|
agentlog import windsurf ~/Downloads/cascade-chat-conversation.md
|
|
@@ -10363,7 +11029,8 @@ Details:
|
|
|
10363
11029
|
--dry-run shows what would be imported without writing archive files.
|
|
10364
11030
|
--explain-skips includes per-session skip reasons for supported sources.
|
|
10365
11031
|
--json prints machine-readable import results.
|
|
10366
|
-
|
|
11032
|
+
ChatGPT and Claude.ai imports print export instructions when the path is omitted.
|
|
11033
|
+
Windsurf imports prompt for the export path when omitted in a terminal.
|
|
10367
11034
|
Windsurf local cache scanning is disabled because current Cascade transcripts are encrypted binary stores. Use the Windsurf "Download trajectory" Markdown export with \`agentlog import windsurf <file-or-folder>\`.
|
|
10368
11035
|
See docs/history-source-handling.md for source-specific storage paths.
|
|
10369
11036
|
`;
|