agentel 0.2.8 → 0.3.1
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 +238 -68
- package/docs/code-reference.md +165 -37
- package/docs/history-source-handling.md +555 -124
- package/docs/release.md +35 -8
- package/npm-shrinkwrap.json +478 -0
- package/package.json +18 -5
- package/scripts/postinstall.js +156 -0
- package/src/archive.js +1176 -65
- package/src/canonical-events.js +346 -35
- package/src/cli.js +7801 -874
- package/src/collector.js +42 -4
- package/src/config.js +51 -4
- package/src/diffs.js +156 -0
- package/src/doctor.js +48 -5
- package/src/importers/claude.js +51 -4
- package/src/importers/copilot.js +385 -0
- package/src/importers/cursor-recovery.js +22 -0
- package/src/importers/factory.js +396 -0
- package/src/importers/gemini.js +39 -0
- package/src/importers/grok.js +367 -0
- package/src/importers/pi.js +422 -0
- package/src/importers/providers.js +64 -5
- package/src/importers.js +4524 -383
- package/src/mcp.js +1 -0
- package/src/memory-sources.js +671 -0
- package/src/memory-store.js +0 -0
- package/src/parser-versions.js +13 -0
- package/src/pricing.js +84 -0
- package/src/search.js +256 -70
- package/src/session-store.js +405 -0
- package/src/slack-notify.js +732 -0
- package/src/source-watch.js +293 -0
- package/src/sources.js +60 -11
- package/src/supervisor.js +231 -7
- package/src/sync.js +6 -0
- package/src/unavailable-sources.js +358 -0
package/src/collector.js
CHANGED
|
@@ -29,6 +29,11 @@ function startCollector(env = process.env, options = {}) {
|
|
|
29
29
|
writeJsonResponse(res, { partialSuccess: {}, agentlog: { stored: record.file } });
|
|
30
30
|
})
|
|
31
31
|
.catch((error) => {
|
|
32
|
+
if (error && error.code === "PAYLOAD_TOO_LARGE") {
|
|
33
|
+
writeJsonResponse(res, { error: error.message }, 413);
|
|
34
|
+
res.once("finish", () => req.destroy());
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
32
37
|
writeJsonResponse(res, { error: error.message }, 500);
|
|
33
38
|
});
|
|
34
39
|
});
|
|
@@ -80,12 +85,44 @@ function writeTelemetryPayload(req, body, env = process.env) {
|
|
|
80
85
|
return { file: payloadFile };
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
|
|
88
|
+
// OTLP batches from local agents are typically well under a megabyte; the cap
|
|
89
|
+
// only exists so a misconfigured or hostile client cannot balloon collector
|
|
90
|
+
// memory with one unbounded request body.
|
|
91
|
+
const COLLECTOR_MAX_BODY_BYTES = 32 * 1024 * 1024;
|
|
92
|
+
|
|
93
|
+
function collectBody(req, maxBytes = COLLECTOR_MAX_BODY_BYTES) {
|
|
84
94
|
return new Promise((resolve, reject) => {
|
|
85
95
|
const chunks = [];
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
let received = 0;
|
|
97
|
+
const cleanup = () => {
|
|
98
|
+
req.removeListener("data", onData);
|
|
99
|
+
req.removeListener("end", onEnd);
|
|
100
|
+
req.removeListener("error", onError);
|
|
101
|
+
};
|
|
102
|
+
const onData = (chunk) => {
|
|
103
|
+
received += chunk.length;
|
|
104
|
+
if (received > maxBytes) {
|
|
105
|
+
// Stop buffering but leave the socket alive so the 413 response can
|
|
106
|
+
// still be written; the handler destroys the connection afterwards.
|
|
107
|
+
cleanup();
|
|
108
|
+
const error = new Error(`request body exceeds ${maxBytes} bytes`);
|
|
109
|
+
error.code = "PAYLOAD_TOO_LARGE";
|
|
110
|
+
reject(error);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
chunks.push(chunk);
|
|
114
|
+
};
|
|
115
|
+
const onEnd = () => {
|
|
116
|
+
cleanup();
|
|
117
|
+
resolve(Buffer.concat(chunks));
|
|
118
|
+
};
|
|
119
|
+
const onError = (error) => {
|
|
120
|
+
cleanup();
|
|
121
|
+
reject(error);
|
|
122
|
+
};
|
|
123
|
+
req.on("data", onData);
|
|
124
|
+
req.on("end", onEnd);
|
|
125
|
+
req.on("error", onError);
|
|
89
126
|
});
|
|
90
127
|
}
|
|
91
128
|
|
|
@@ -108,6 +145,7 @@ function looksLikeJson(body) {
|
|
|
108
145
|
}
|
|
109
146
|
|
|
110
147
|
module.exports = {
|
|
148
|
+
collectBody,
|
|
111
149
|
startCollector,
|
|
112
150
|
writeTelemetryPayload
|
|
113
151
|
};
|
package/src/config.js
CHANGED
|
@@ -4,7 +4,7 @@ const crypto = require("crypto");
|
|
|
4
4
|
const os = require("os");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const { ensureBaseDirs, paths, readJson, writeJson } = require("./paths");
|
|
7
|
-
const { IMPORT_SOURCE_ORDER, enabledImportSources } = require("./sources");
|
|
7
|
+
const { IMPORT_SOURCE_ORDER, canonicalImportSource, enabledImportSources } = require("./sources");
|
|
8
8
|
|
|
9
9
|
function defaultConfig(env = process.env) {
|
|
10
10
|
const p = paths(env);
|
|
@@ -53,6 +53,27 @@ function defaultConfig(env = process.env) {
|
|
|
53
53
|
webChatsDefaultScope: "local",
|
|
54
54
|
revealCache: true
|
|
55
55
|
},
|
|
56
|
+
artifacts: {
|
|
57
|
+
// Copy images/PDFs referenced by tool calls (screenshots in /tmp etc.)
|
|
58
|
+
// into the archive before they are deleted. Off by default because it
|
|
59
|
+
// grows the archive beyond conversation text.
|
|
60
|
+
enabled: false,
|
|
61
|
+
maxFileBytes: 25 * 1024 * 1024,
|
|
62
|
+
maxSessionBytes: 100 * 1024 * 1024
|
|
63
|
+
},
|
|
64
|
+
notify: {
|
|
65
|
+
// Posting publishes session content beyond this machine; stays off
|
|
66
|
+
// until explicitly configured via `agentlog notify slack setup`.
|
|
67
|
+
// `summary` and `stream` (firehose) toggle independently with
|
|
68
|
+
// independent channels.
|
|
69
|
+
slack: {
|
|
70
|
+
enabled: false,
|
|
71
|
+
repos: [],
|
|
72
|
+
botToken: "",
|
|
73
|
+
summary: { enabled: true, channel: "", quietMinutes: 10 },
|
|
74
|
+
stream: { enabled: false, channel: "", batchSeconds: 45 }
|
|
75
|
+
}
|
|
76
|
+
},
|
|
56
77
|
createdAt: new Date().toISOString()
|
|
57
78
|
};
|
|
58
79
|
}
|
|
@@ -72,12 +93,36 @@ function loadConfig(env = process.env) {
|
|
|
72
93
|
sync: { ...defaults.sync, ...(cfg.sync || {}) },
|
|
73
94
|
index: { ...defaults.index, ...(cfg.index || {}) },
|
|
74
95
|
imports: { ...defaults.imports, ...(cfg.imports || {}) },
|
|
75
|
-
privacy: { ...defaults.privacy, ...(cfg.privacy || {}) }
|
|
96
|
+
privacy: { ...defaults.privacy, ...(cfg.privacy || {}) },
|
|
97
|
+
artifacts: { ...defaults.artifacts, ...(cfg.artifacts || {}) },
|
|
98
|
+
notify: {
|
|
99
|
+
...defaults.notify,
|
|
100
|
+
...(cfg.notify || {}),
|
|
101
|
+
slack: mergeSlackNotifyConfig(defaults.notify.slack, cfg.notify?.slack)
|
|
102
|
+
}
|
|
76
103
|
};
|
|
77
104
|
merged.imports.sources = enabledImportSources(merged.imports.sources);
|
|
78
105
|
return merged;
|
|
79
106
|
}
|
|
80
107
|
|
|
108
|
+
// Configs written before the summary/stream split kept channel and
|
|
109
|
+
// quietMinutes at the slack top level; lift them into the summary block so
|
|
110
|
+
// the defaults merge cannot shadow them.
|
|
111
|
+
function mergeSlackNotifyConfig(defaults, raw) {
|
|
112
|
+
const slack = raw || {};
|
|
113
|
+
const summary = { ...defaults.summary, ...(slack.summary || {}) };
|
|
114
|
+
if (!slack.summary) {
|
|
115
|
+
if (slack.channel) summary.channel = slack.channel;
|
|
116
|
+
if (slack.quietMinutes) summary.quietMinutes = slack.quietMinutes;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...defaults,
|
|
120
|
+
...slack,
|
|
121
|
+
summary,
|
|
122
|
+
stream: { ...defaults.stream, ...(slack.stream || {}) }
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
81
126
|
function effectiveImportSources(config) {
|
|
82
127
|
const imports = config?.imports || {};
|
|
83
128
|
const configured = enabledImportSources(Array.isArray(imports.sources) ? imports.sources : []);
|
|
@@ -271,9 +316,10 @@ function normalizeImportSources(value) {
|
|
|
271
316
|
.split(/[,\s]+/)
|
|
272
317
|
.map((item) => item.trim())
|
|
273
318
|
.filter(Boolean);
|
|
274
|
-
const
|
|
319
|
+
const canonicalRaw = raw.map(canonicalImportSource);
|
|
320
|
+
const selected = new Set(canonicalRaw);
|
|
275
321
|
const ordered = IMPORT_SOURCE_ORDER.filter((source) => selected.has(source));
|
|
276
|
-
const extras =
|
|
322
|
+
const extras = canonicalRaw.filter((source) => !ordered.includes(source));
|
|
277
323
|
return enabledImportSources([...ordered, ...extras]);
|
|
278
324
|
}
|
|
279
325
|
|
|
@@ -288,6 +334,7 @@ module.exports = {
|
|
|
288
334
|
effectiveImportSources,
|
|
289
335
|
getConfigKey,
|
|
290
336
|
initConfig,
|
|
337
|
+
defaultDeviceName,
|
|
291
338
|
loadConfig,
|
|
292
339
|
normalizeRemoteEndpointInput,
|
|
293
340
|
saveConfig,
|
package/src/diffs.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const MAX_STRUCTURED_PATCH_HUNKS = 2000;
|
|
4
|
+
const MAX_STRUCTURED_PATCH_LINES = 50000;
|
|
5
|
+
const MAX_STRUCTURED_DIFF_CHARS = 2 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
function normalizeStructuredPatch(value) {
|
|
8
|
+
if (!Array.isArray(value) || !value.length) return [];
|
|
9
|
+
const hunks = [];
|
|
10
|
+
let lineCount = 0;
|
|
11
|
+
for (const raw of value) {
|
|
12
|
+
if (!raw || typeof raw !== "object") continue;
|
|
13
|
+
const lines = Array.isArray(raw.lines)
|
|
14
|
+
? raw.lines.map((line) => String(line == null ? "" : line)).filter((line) => /^[ +\\-]/.test(line))
|
|
15
|
+
: [];
|
|
16
|
+
if (!lines.length) continue;
|
|
17
|
+
lineCount += lines.length;
|
|
18
|
+
if (lineCount > MAX_STRUCTURED_PATCH_LINES) break;
|
|
19
|
+
const hunk = {
|
|
20
|
+
oldStart: positiveInt(raw.oldStart ?? raw.old_start) || 1,
|
|
21
|
+
newStart: positiveInt(raw.newStart ?? raw.new_start) || 1,
|
|
22
|
+
lines
|
|
23
|
+
};
|
|
24
|
+
const file = firstString(raw.file, raw.path, raw.filePath, raw.filename);
|
|
25
|
+
if (file) hunk.file = file;
|
|
26
|
+
if (Number.isFinite(Number(raw.oldLines ?? raw.old_lines))) hunk.oldLines = Math.max(0, Math.floor(Number(raw.oldLines ?? raw.old_lines)));
|
|
27
|
+
if (Number.isFinite(Number(raw.newLines ?? raw.new_lines))) hunk.newLines = Math.max(0, Math.floor(Number(raw.newLines ?? raw.new_lines)));
|
|
28
|
+
hunks.push(hunk);
|
|
29
|
+
if (hunks.length >= MAX_STRUCTURED_PATCH_HUNKS) break;
|
|
30
|
+
}
|
|
31
|
+
return hunks;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function structuredPatchFromToolArguments(args) {
|
|
35
|
+
if (!args) return [];
|
|
36
|
+
if (typeof args === "string") return structuredPatchFromDiffText(args);
|
|
37
|
+
if (Array.isArray(args)) {
|
|
38
|
+
const hunks = [];
|
|
39
|
+
for (const item of args) {
|
|
40
|
+
hunks.push(...structuredPatchFromToolArguments(item));
|
|
41
|
+
if (hunks.length >= MAX_STRUCTURED_PATCH_HUNKS) break;
|
|
42
|
+
}
|
|
43
|
+
return hunks.slice(0, MAX_STRUCTURED_PATCH_HUNKS);
|
|
44
|
+
}
|
|
45
|
+
if (typeof args !== "object") return [];
|
|
46
|
+
const existing = normalizeStructuredPatch(args.structuredPatch || args.structured_patch);
|
|
47
|
+
if (existing.length) return existing;
|
|
48
|
+
const hunks = [];
|
|
49
|
+
for (const key of ["diff", "patch", "input", "text", "content"]) {
|
|
50
|
+
if (typeof args[key] === "string") hunks.push(...structuredPatchFromDiffText(args[key], { defaultFile: firstString(args.path, args.file, args.file_path, args.filePath) }));
|
|
51
|
+
if (hunks.length >= MAX_STRUCTURED_PATCH_HUNKS) break;
|
|
52
|
+
}
|
|
53
|
+
if (!hunks.length) {
|
|
54
|
+
for (const key of ["changes", "edits", "hunks", "diffs", "fileDiffs", "file_diffs"]) {
|
|
55
|
+
if (args[key] != null) hunks.push(...structuredPatchFromToolArguments(args[key]));
|
|
56
|
+
if (hunks.length >= MAX_STRUCTURED_PATCH_HUNKS) break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return hunks.slice(0, MAX_STRUCTURED_PATCH_HUNKS);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function structuredPatchFromDiffText(diff, options = {}) {
|
|
63
|
+
const text = String(diff || "");
|
|
64
|
+
if (!text.trim() || text.length > MAX_STRUCTURED_DIFF_CHARS) return [];
|
|
65
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
66
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
67
|
+
const hunkRe = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/;
|
|
68
|
+
const hunks = [];
|
|
69
|
+
let current = null;
|
|
70
|
+
let currentFile = firstString(options.defaultFile);
|
|
71
|
+
let lineCount = 0;
|
|
72
|
+
|
|
73
|
+
const finish = () => {
|
|
74
|
+
if (!current || !current.lines.length) {
|
|
75
|
+
current = null;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
hunks.push(current);
|
|
79
|
+
current = null;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (const rawLine of lines) {
|
|
83
|
+
const line = String(rawLine);
|
|
84
|
+
const hunk = hunkRe.exec(line);
|
|
85
|
+
if (hunk) {
|
|
86
|
+
finish();
|
|
87
|
+
current = {
|
|
88
|
+
oldStart: Number(hunk[1]),
|
|
89
|
+
newStart: Number(hunk[3]),
|
|
90
|
+
oldLines: hunk[2] == null ? 1 : Number(hunk[2]),
|
|
91
|
+
newLines: hunk[4] == null ? 1 : Number(hunk[4]),
|
|
92
|
+
lines: []
|
|
93
|
+
};
|
|
94
|
+
if (currentFile) current.file = currentFile;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const nextFile = diffMetadataFile(line);
|
|
99
|
+
if (nextFile) {
|
|
100
|
+
finish();
|
|
101
|
+
currentFile = nextFile;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (line.startsWith("\")) continue;
|
|
105
|
+
|
|
106
|
+
if (!current) continue;
|
|
107
|
+
if (line.startsWith("+") && !line.startsWith("+++")) current.lines.push(line);
|
|
108
|
+
else if (line.startsWith("-") && !line.startsWith("---")) current.lines.push(line);
|
|
109
|
+
else if (line.startsWith(" ")) current.lines.push(line);
|
|
110
|
+
else current.lines.push(" " + line);
|
|
111
|
+
lineCount += 1;
|
|
112
|
+
if (lineCount > MAX_STRUCTURED_PATCH_LINES || hunks.length >= MAX_STRUCTURED_PATCH_HUNKS) break;
|
|
113
|
+
}
|
|
114
|
+
finish();
|
|
115
|
+
return normalizeStructuredPatch(hunks);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function diffMetadataFile(line) {
|
|
119
|
+
const text = String(line || "");
|
|
120
|
+
const git = text.match(/^diff --git\s+a\/(.+?)\s+b\/(.+)$/);
|
|
121
|
+
if (git) return stripDiffFilePrefix(git[2]);
|
|
122
|
+
const update = text.match(/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s+(.+)$/);
|
|
123
|
+
if (update) return update[1].trim();
|
|
124
|
+
const plus = text.match(/^\+\+\+\s+(.+)$/);
|
|
125
|
+
if (plus) {
|
|
126
|
+
const file = stripDiffFilePrefix(plus[1]);
|
|
127
|
+
return file === "/dev/null" ? "" : file;
|
|
128
|
+
}
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function stripDiffFilePrefix(value) {
|
|
133
|
+
let text = String(value || "").trim();
|
|
134
|
+
if (!text) return "";
|
|
135
|
+
text = text.split(/\t/)[0].trim();
|
|
136
|
+
if ((text.startsWith("a/") || text.startsWith("b/")) && text.length > 2) return text.slice(2);
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function positiveInt(value) {
|
|
141
|
+
const number = Math.floor(Number(value));
|
|
142
|
+
return Number.isFinite(number) && number > 0 ? number : 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function firstString(...values) {
|
|
146
|
+
for (const value of values) {
|
|
147
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
148
|
+
}
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
normalizeStructuredPatch,
|
|
154
|
+
structuredPatchFromDiffText,
|
|
155
|
+
structuredPatchFromToolArguments
|
|
156
|
+
};
|
package/src/doctor.js
CHANGED
|
@@ -9,6 +9,7 @@ const { discoverCliHistory } = require("./importers");
|
|
|
9
9
|
const { canonicalSourceType, parserVersionForSource } = require("./parser-versions");
|
|
10
10
|
const { paths, readJson } = require("./paths");
|
|
11
11
|
const { hasRemoteTarget } = require("./sync");
|
|
12
|
+
const { CLAUDE_CODE_REPAIR_COMMAND, CLAUDE_CODE_REPAIR_PREVIEW_COMMAND, summarizeUnavailableSourceSessions, unavailableSourceSessions } = require("./unavailable-sources");
|
|
12
13
|
|
|
13
14
|
function runDoctor(env = process.env, options = {}) {
|
|
14
15
|
reportProgress(options, "Doctor", "checking configuration", 1, 5);
|
|
@@ -30,6 +31,14 @@ function runDoctor(env = process.env, options = {}) {
|
|
|
30
31
|
reportProgress(options, "Doctor", "checking parser versions", 4, 5);
|
|
31
32
|
const parsers = parserVersionHealth(env);
|
|
32
33
|
add(checks, "parser versions", parsers.outdated.length === 0, parserVersionSummary(parsers), parserVersionRemediation(parsers));
|
|
34
|
+
const unavailableSources = summarizeUnavailableSourceSessions(unavailableSourceSessions(env), { env });
|
|
35
|
+
add(
|
|
36
|
+
checks,
|
|
37
|
+
"unavailable sources",
|
|
38
|
+
unavailableSources.totalSessions === 0,
|
|
39
|
+
unavailableSourceSummary(unavailableSources),
|
|
40
|
+
unavailableSourceRemediation(unavailableSources)
|
|
41
|
+
);
|
|
33
42
|
|
|
34
43
|
reportProgress(options, "Doctor", "discovering local sources", 5, 5);
|
|
35
44
|
const discovery = discoverCliHistory(env, { onProgress: options.onProgress });
|
|
@@ -49,6 +58,7 @@ function runDoctor(env = process.env, options = {}) {
|
|
|
49
58
|
trackedFiles: Object.keys(importState.files || {}).length
|
|
50
59
|
},
|
|
51
60
|
coverage,
|
|
61
|
+
unavailableSources,
|
|
52
62
|
parsers,
|
|
53
63
|
recommendations
|
|
54
64
|
};
|
|
@@ -62,11 +72,18 @@ function sourceCoverage(discovery, cfg) {
|
|
|
62
72
|
coverageRow("codex-sdk", "Codex SDK jobs", discovery.codexSdk, configured),
|
|
63
73
|
coverageRow("claude", "Claude Code CLI", discovery.claude, configured),
|
|
64
74
|
coverageRow("claude-code-desktop", "Claude Code Desktop", discovery.claudeCodeDesktop, configured),
|
|
65
|
-
coverageRow("claude-
|
|
75
|
+
coverageRow("claude-cowork", "Claude Cowork", discovery.claudeWorkspace, configured),
|
|
66
76
|
coverageRow("claude-sdk", "Claude SDK jobs", discovery.claudeSdk, configured),
|
|
67
77
|
coverageRow("gemini-cli", "Gemini CLI", discovery.geminiCli, configured),
|
|
68
|
-
coverageRow("antigravity", "Antigravity", discovery.
|
|
78
|
+
coverageRow("antigravity-cli", "Antigravity CLI", discovery.antigravityCli, configured),
|
|
79
|
+
coverageRow("antigravity", "Antigravity 2.0", discovery.antigravity, configured),
|
|
80
|
+
coverageRow("antigravity-ide", "Antigravity IDE", discovery.antigravityIde, configured),
|
|
69
81
|
coverageRow("devin-cli", "Devin CLI", discovery.devinCli, configured),
|
|
82
|
+
coverageRow("devin-desktop", "Devin Desktop", discovery.devinDesktop, configured),
|
|
83
|
+
coverageRow("copilot-cli", "GitHub Copilot CLI", discovery.copilotCli, configured),
|
|
84
|
+
coverageRow("factory", "Factory Droid", discovery.factory, configured),
|
|
85
|
+
coverageRow("grok-build", "Grok Build", discovery.grokBuild, configured),
|
|
86
|
+
coverageRow("pi", "pi", discovery.pi, configured),
|
|
70
87
|
coverageRow("cursor", "Cursor", discovery.cursor, configured),
|
|
71
88
|
coverageRow("cline", "Cline", discovery.cline, configured),
|
|
72
89
|
coverageRow("opencode-cli", "OpenCode CLI", discovery.opencodeCli, configured),
|
|
@@ -86,7 +103,8 @@ function coverageRow(source, label, result = {}, configured) {
|
|
|
86
103
|
label,
|
|
87
104
|
configured: configured.has(source)
|
|
88
105
|
|| (source === "claude-code-desktop" && configured.has("claude-desktop"))
|
|
89
|
-
|| (source === "claude-
|
|
106
|
+
|| (source === "claude-cowork" && configured.has("claude-desktop"))
|
|
107
|
+
|| (source === "claude-cowork" && configured.has("claude-workspace"))
|
|
90
108
|
|| (source.startsWith("opencode-") && configured.has("opencode")),
|
|
91
109
|
sessions: result?.sessions || 0,
|
|
92
110
|
oldest: result?.oldest || "",
|
|
@@ -145,6 +163,20 @@ function parserVersionRemediation(parsers) {
|
|
|
145
163
|
return "Run the affected import commands listed in Recommendations.";
|
|
146
164
|
}
|
|
147
165
|
|
|
166
|
+
function unavailableSourceSummary(summary) {
|
|
167
|
+
if (!summary?.totalSessions) return "no preserved archives depend on missing source files";
|
|
168
|
+
const missing = summary.totalMissingSources ? `; ${summary.totalMissingSources} missing source path${summary.totalMissingSources === 1 ? "" : "s"}` : "";
|
|
169
|
+
return `${summary.totalSessions} preserved unavailable source session${summary.totalSessions === 1 ? "" : "s"}${missing}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function unavailableSourceRemediation(summary) {
|
|
173
|
+
if (!summary?.totalSessions) return "";
|
|
174
|
+
if (summary.claudeCodeRepair?.restoreCount) {
|
|
175
|
+
return `Run \`${CLAUDE_CODE_REPAIR_PREVIEW_COMMAND}\`, then \`${CLAUDE_CODE_REPAIR_COMMAND} --yes\`, then \`agentlog update --yes --since all --sources claude\`.`;
|
|
176
|
+
}
|
|
177
|
+
return "Restore the missing source application history from backup, then run `agentlog update --yes --since all`; otherwise keep the preserved archive as historical data.";
|
|
178
|
+
}
|
|
179
|
+
|
|
148
180
|
function buildRecommendations(checks, coverage, parsers) {
|
|
149
181
|
const recommendations = [];
|
|
150
182
|
for (const check of checks) {
|
|
@@ -199,12 +231,17 @@ function parserUpdateCommand(sourceType) {
|
|
|
199
231
|
"cli-history": "claude",
|
|
200
232
|
"claude-sdk-history": "claude-sdk",
|
|
201
233
|
"claude-code-desktop-metadata": "claude-code-desktop",
|
|
202
|
-
"claude-workspace-desktop": "claude-
|
|
234
|
+
"claude-workspace-desktop": "claude-cowork",
|
|
203
235
|
"cursor-workspace-sqlite": "cursor",
|
|
204
236
|
"cursor-global-sqlite": "cursor",
|
|
205
237
|
"cursor-raw-sqlite-salvage": "cursor",
|
|
206
238
|
"cursor-agent-transcripts": "cursor",
|
|
207
239
|
"devin-cli-history": "devin-cli",
|
|
240
|
+
"devin-desktop-acp-events": "devin-desktop",
|
|
241
|
+
"copilot-cli-history": "copilot-cli",
|
|
242
|
+
"factory-droid-history": "factory",
|
|
243
|
+
"grok-build-history": "grok-build",
|
|
244
|
+
"pi-cli-history": "pi",
|
|
208
245
|
"gemini-cli-history": "gemini-cli",
|
|
209
246
|
"cline-task-history": "cline",
|
|
210
247
|
"opencode-cli-history": "opencode-cli",
|
|
@@ -215,7 +252,13 @@ function parserUpdateCommand(sourceType) {
|
|
|
215
252
|
"opencode-history": "opencode",
|
|
216
253
|
"opencode-sqlite-history": "opencode",
|
|
217
254
|
"aider-chat-history": "aider",
|
|
218
|
-
"antigravity-history": "antigravity"
|
|
255
|
+
"antigravity-history": "antigravity",
|
|
256
|
+
"antigravity-transcript-log": "antigravity",
|
|
257
|
+
"antigravity-brain": "antigravity",
|
|
258
|
+
"antigravity-cli-transcript-log": "antigravity-cli",
|
|
259
|
+
"antigravity-cli-brain": "antigravity-cli",
|
|
260
|
+
"antigravity-ide-transcript-log": "antigravity-ide",
|
|
261
|
+
"antigravity-ide-brain": "antigravity-ide"
|
|
219
262
|
}[canonicalSourceType(sourceType)];
|
|
220
263
|
return source ? `agentlog import --source ${source} --since all` : "";
|
|
221
264
|
}
|
package/src/importers/claude.js
CHANGED
|
@@ -50,7 +50,20 @@ function assistantMessages(event, message, provider, context, timestamp, content
|
|
|
50
50
|
toolCalls: toolCalls.length ? toolCalls : undefined
|
|
51
51
|
};
|
|
52
52
|
const result = [];
|
|
53
|
-
|
|
53
|
+
const hasVisibleAssistantWork = Boolean(text || toolCalls.length);
|
|
54
|
+
if (thinking) {
|
|
55
|
+
result.push(
|
|
56
|
+
supplementaryMessage(
|
|
57
|
+
provider,
|
|
58
|
+
"Claude thinking",
|
|
59
|
+
thinking,
|
|
60
|
+
timestamp,
|
|
61
|
+
"thinking",
|
|
62
|
+
metadata.model,
|
|
63
|
+
hasVisibleAssistantWork ? {} : compactMetadata({ ...metadata, toolCalls: undefined })
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
54
67
|
if (text || toolCalls.length) result.push({ role: "assistant", content: text, timestamp, metadata });
|
|
55
68
|
return result;
|
|
56
69
|
}
|
|
@@ -102,6 +115,7 @@ function claudeEventMetadata(event) {
|
|
|
102
115
|
parentToolUseID: firstString(event.parentToolUseID, event.parent_tool_use_id),
|
|
103
116
|
toolUseID: firstString(event.toolUseID, event.tool_use_id),
|
|
104
117
|
isSidechain: typeof event.isSidechain === "boolean" ? event.isSidechain : undefined,
|
|
118
|
+
isMeta: typeof event.isMeta === "boolean" ? event.isMeta : (typeof event.payload?.isMeta === "boolean" ? event.payload.isMeta : undefined),
|
|
105
119
|
isVisibleInTranscriptOnly: typeof event.isVisibleInTranscriptOnly === "boolean" ? event.isVisibleInTranscriptOnly : undefined,
|
|
106
120
|
isCompactSummary: typeof event.isCompactSummary === "boolean" ? event.isCompactSummary : undefined,
|
|
107
121
|
userType: firstString(event.userType, event.user_type),
|
|
@@ -133,13 +147,14 @@ function claudeApiErrorMetadata(event) {
|
|
|
133
147
|
});
|
|
134
148
|
}
|
|
135
149
|
|
|
136
|
-
function supplementaryMessage(provider, title, content, timestamp, summaryKind, model = "") {
|
|
150
|
+
function supplementaryMessage(provider, title, content, timestamp, summaryKind, model = "", metadata = {}) {
|
|
137
151
|
return {
|
|
138
152
|
role: "assistant",
|
|
139
153
|
content: `### ${title}\n\n${String(content || "").trim()}`,
|
|
140
154
|
timestamp,
|
|
141
155
|
metadata: {
|
|
142
156
|
provider,
|
|
157
|
+
...compactMetadata(metadata),
|
|
143
158
|
eventType: `claude-${summaryKind}`,
|
|
144
159
|
supplementary: true,
|
|
145
160
|
summaryKind,
|
|
@@ -236,6 +251,15 @@ function remoteFileHistorySnapshotMessage(event, provider, timestamp) {
|
|
|
236
251
|
const snapshotTimestamp = timestamp || eventTimestamp(snapshot);
|
|
237
252
|
const backups = snapshot.trackedFileBackups && typeof snapshot.trackedFileBackups === "object" ? snapshot.trackedFileBackups : {};
|
|
238
253
|
const paths = Object.keys(backups).sort();
|
|
254
|
+
const backupEntries = paths.map((filePath) => {
|
|
255
|
+
const backup = backups[filePath] && typeof backups[filePath] === "object" ? backups[filePath] : {};
|
|
256
|
+
return compactMetadata({
|
|
257
|
+
path: filePath,
|
|
258
|
+
backupFileName: firstString(backup.backupFileName, backup.backup_file_name) || undefined,
|
|
259
|
+
version: numberValue(backup.version) ?? undefined,
|
|
260
|
+
backupTime: firstString(backup.backupTime, backup.backup_time) || undefined
|
|
261
|
+
});
|
|
262
|
+
});
|
|
239
263
|
const action = event.isSnapshotUpdate ? "updated" : "recorded";
|
|
240
264
|
const target = paths.length ? `: ${namesPreview(paths)}` : "";
|
|
241
265
|
return remoteContextMessage(provider, event, snapshotTimestamp, "remote_control_file_history", `Remote Control file history snapshot ${action}${target}`, {
|
|
@@ -244,7 +268,8 @@ function remoteFileHistorySnapshotMessage(event, provider, timestamp) {
|
|
|
244
268
|
isSnapshotUpdate: typeof event.isSnapshotUpdate === "boolean" ? event.isSnapshotUpdate : undefined,
|
|
245
269
|
timestamp: snapshotTimestamp || undefined,
|
|
246
270
|
backupFileCount: paths.length || undefined,
|
|
247
|
-
paths: paths.length ? paths : undefined
|
|
271
|
+
paths: paths.length ? paths : undefined,
|
|
272
|
+
backups: backupEntries.length ? backupEntries : undefined
|
|
248
273
|
})
|
|
249
274
|
});
|
|
250
275
|
}
|
|
@@ -454,10 +479,32 @@ function normalizeToolResult(part, provider, event, context = {}) {
|
|
|
454
479
|
collapsed: output.split("\n").length > 18,
|
|
455
480
|
status: part.is_error ? "error" : "completed",
|
|
456
481
|
sourceToolUseID: firstString(event?.sourceToolUseID, event?.source_tool_use_id) || undefined,
|
|
457
|
-
structuredContent: event?.mcpMeta?.structuredContent && typeof event.mcpMeta.structuredContent === "object" ? event.mcpMeta.structuredContent : undefined
|
|
482
|
+
structuredContent: event?.mcpMeta?.structuredContent && typeof event.mcpMeta.structuredContent === "object" ? event.mcpMeta.structuredContent : undefined,
|
|
483
|
+
structuredPatch: sanitizeStructuredPatch(event?.toolUseResult?.structuredPatch)
|
|
458
484
|
});
|
|
459
485
|
}
|
|
460
486
|
|
|
487
|
+
// Claude Code's Edit/Write results include jsdiff-style hunks with absolute
|
|
488
|
+
// file line numbers; keep them so viewers can render numbered diffs.
|
|
489
|
+
function sanitizeStructuredPatch(hunks) {
|
|
490
|
+
if (!Array.isArray(hunks) || !hunks.length) return undefined;
|
|
491
|
+
const sanitized = [];
|
|
492
|
+
for (const hunk of hunks) {
|
|
493
|
+
if (!hunk || typeof hunk !== "object" || !Array.isArray(hunk.lines)) continue;
|
|
494
|
+
const oldStart = Number(hunk.oldStart);
|
|
495
|
+
const newStart = Number(hunk.newStart);
|
|
496
|
+
if (!Number.isFinite(oldStart) || !Number.isFinite(newStart)) continue;
|
|
497
|
+
sanitized.push({
|
|
498
|
+
oldStart,
|
|
499
|
+
oldLines: Number(hunk.oldLines) || 0,
|
|
500
|
+
newStart,
|
|
501
|
+
newLines: Number(hunk.newLines) || 0,
|
|
502
|
+
lines: hunk.lines.map((line) => String(line ?? ""))
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return sanitized.length ? sanitized : undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
461
508
|
function extractToolResultOutput(part, eventToolUseResult) {
|
|
462
509
|
const direct = extractText(part.content ?? part.output ?? part.result ?? part.text);
|
|
463
510
|
const extra = extractText(eventToolUseResult?.stdout ?? eventToolUseResult?.stderr ?? eventToolUseResult?.output ?? eventToolUseResult?.content);
|