agentel 0.2.0 → 0.2.2
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 +161 -63
- package/agentlog-spec.md +42 -35
- package/bin/agentlog-recall.js +2 -0
- package/bin/agentlog.js +12 -0
- package/docs/code-reference.md +120 -34
- package/docs/history-source-handling.md +236 -81
- package/docs/release.md +8 -8
- package/package.json +5 -4
- package/src/archive.js +278 -20
- package/src/cli.js +3457 -511
- package/src/config.js +42 -1
- package/src/doctor.js +167 -10
- package/src/importers/gemini.js +369 -7
- package/src/importers.js +1837 -135
- package/src/mcp.js +4 -1
- package/src/parser-versions.js +37 -22
- package/src/paths.js +4 -2
- package/src/redaction.js +140 -17
- package/src/search.js +671 -52
- package/src/supervisor.js +206 -57
- package/src/sync.js +459 -12
package/src/mcp.js
CHANGED
|
@@ -60,7 +60,10 @@ async function handleRequest(message, env = process.env) {
|
|
|
60
60
|
{
|
|
61
61
|
repo: args.repo,
|
|
62
62
|
limit: args.limit || 10,
|
|
63
|
-
includeWebChats: args.include_web_chats === true
|
|
63
|
+
includeWebChats: args.include_web_chats === true,
|
|
64
|
+
noRebuild: true,
|
|
65
|
+
skipJsonIndex: true,
|
|
66
|
+
allowStaleFts: true
|
|
64
67
|
},
|
|
65
68
|
env
|
|
66
69
|
);
|
package/src/parser-versions.js
CHANGED
|
@@ -1,34 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const { version: PACKAGE_VERSION } = require("../package.json");
|
|
4
|
+
|
|
3
5
|
const SOURCE_TYPE_ALIASES = {
|
|
4
6
|
"cursor-sqlite-history": "cursor-workspace-sqlite",
|
|
5
7
|
"antigravity-brain": "antigravity-history"
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
const
|
|
9
|
-
"codex-cli-history":
|
|
10
|
-
"codex-desktop-history":
|
|
11
|
-
"cli-history":
|
|
12
|
-
"claude-sdk-history":
|
|
13
|
-
"claude-code-desktop-metadata":
|
|
14
|
-
"claude-workspace-desktop":
|
|
15
|
-
"cursor-workspace-sqlite":
|
|
16
|
-
"cursor-global-sqlite":
|
|
17
|
-
"cursor-raw-sqlite-salvage":
|
|
18
|
-
"cursor-agent-transcripts":
|
|
19
|
-
"devin-cli-history":
|
|
20
|
-
"gemini-cli-history":
|
|
21
|
-
"cline-task-history":
|
|
22
|
-
"opencode-history":
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
10
|
+
const PARSER_VERSION_SUFFIXES = {
|
|
11
|
+
"codex-cli-history": 0,
|
|
12
|
+
"codex-desktop-history": 0,
|
|
13
|
+
"cli-history": 0,
|
|
14
|
+
"claude-sdk-history": 0,
|
|
15
|
+
"claude-code-desktop-metadata": 0,
|
|
16
|
+
"claude-workspace-desktop": 0,
|
|
17
|
+
"cursor-workspace-sqlite": 0,
|
|
18
|
+
"cursor-global-sqlite": 0,
|
|
19
|
+
"cursor-raw-sqlite-salvage": 0,
|
|
20
|
+
"cursor-agent-transcripts": 0,
|
|
21
|
+
"devin-cli-history": 0,
|
|
22
|
+
"gemini-cli-history": 0,
|
|
23
|
+
"cline-task-history": 0,
|
|
24
|
+
"opencode-history": 0,
|
|
25
|
+
"opencode-sqlite-history": 0,
|
|
26
|
+
"aider-chat-history": 0,
|
|
27
|
+
"antigravity-history": 0,
|
|
28
|
+
"antigravity-trajectory-summary": 0,
|
|
29
|
+
"windsurf-trajectory-export": 0,
|
|
30
|
+
"web-chat-export": 0,
|
|
31
|
+
"chatgpt-export": 0,
|
|
32
|
+
"claude-web-export": 0,
|
|
33
|
+
"claude-web-memory": 0,
|
|
34
|
+
import: 0
|
|
30
35
|
};
|
|
31
36
|
|
|
37
|
+
const PARSER_VERSIONS = Object.fromEntries(
|
|
38
|
+
Object.entries(PARSER_VERSION_SUFFIXES).map(([sourceType, suffix]) => [sourceType, parserVersion(suffix)])
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
function parserVersion(suffix = 0) {
|
|
42
|
+
return `${PACKAGE_VERSION}.${Number(suffix) || 0}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
function canonicalSourceType(sourceType) {
|
|
33
46
|
const key = String(sourceType || "import").trim() || "import";
|
|
34
47
|
return SOURCE_TYPE_ALIASES[key] || key;
|
|
@@ -53,6 +66,8 @@ function assertKnownSourceType(sourceType) {
|
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
module.exports = {
|
|
69
|
+
PACKAGE_VERSION,
|
|
70
|
+
PARSER_VERSION_SUFFIXES,
|
|
56
71
|
PARSER_VERSIONS,
|
|
57
72
|
SOURCE_TYPE_ALIASES,
|
|
58
73
|
assertKnownSourceType,
|
package/src/paths.js
CHANGED
|
@@ -23,6 +23,7 @@ function paths(env = process.env) {
|
|
|
23
23
|
imports: path.join(home, "state", "imports.json"),
|
|
24
24
|
webAccounts: path.join(home, "state", "web-accounts.json"),
|
|
25
25
|
index: path.join(home, "data", "agentlog", "indexes", "bm25", "index.json"),
|
|
26
|
+
ftsIndex: path.join(home, "data", "agentlog", "indexes", "fts5", "index.sqlite"),
|
|
26
27
|
revealCache: path.join(home, "cache", "unredacted")
|
|
27
28
|
};
|
|
28
29
|
}
|
|
@@ -46,9 +47,10 @@ function readJson(file, fallback) {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function writeJson(file, value) {
|
|
50
|
+
function writeJson(file, value, options = {}) {
|
|
50
51
|
ensureDir(path.dirname(file));
|
|
51
|
-
|
|
52
|
+
const json = options.pretty === false ? JSON.stringify(value) : JSON.stringify(value, null, 2);
|
|
53
|
+
fs.writeFileSync(file, `${json}\n`, { mode: 0o600 });
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
module.exports = {
|
package/src/redaction.js
CHANGED
|
@@ -16,23 +16,47 @@ const BUILT_INS = [
|
|
|
16
16
|
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
17
17
|
}
|
|
18
18
|
];
|
|
19
|
+
const HIGH_ENTROPY_ENV_BUILT_IN = "high_entropy_env";
|
|
20
|
+
const BUILT_IN_NAMES = [...BUILT_INS.map((item) => item.name), HIGH_ENTROPY_ENV_BUILT_IN];
|
|
21
|
+
const REDACTION_LABELS = {
|
|
22
|
+
aws_key: "AWS ACCESS KEY ID",
|
|
23
|
+
anthropic_key: "ANTHROPIC KEY",
|
|
24
|
+
openai_key: "OPENAI KEY",
|
|
25
|
+
github_token: "GITHUB TOKEN",
|
|
26
|
+
slack_token: "SLACK TOKEN",
|
|
27
|
+
jwt: "JWT",
|
|
28
|
+
private_key: "PRIVATE KEY",
|
|
29
|
+
high_entropy_env: "HIGH ENTROPY ENV VALUE"
|
|
30
|
+
};
|
|
19
31
|
|
|
20
|
-
const REDACTION_MARKER_RE = /\[REDACTED
|
|
32
|
+
const REDACTION_MARKER_RE = /\[REDACTED(?::|\s+)([^\]\n]+)\]/g;
|
|
21
33
|
|
|
22
34
|
function loadRedactionConfig(env = process.env) {
|
|
23
35
|
const file = paths(env).redaction;
|
|
24
36
|
try {
|
|
25
37
|
return parseRedactionYaml(fs.readFileSync(file, "utf8"));
|
|
26
38
|
} catch (error) {
|
|
27
|
-
if (error.code === "ENOENT") return {
|
|
39
|
+
if (error.code === "ENOENT") return defaultRedactionConfig({ enabled: false });
|
|
28
40
|
throw error;
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
function defaultRedactionConfig(options = {}) {
|
|
45
|
+
return {
|
|
46
|
+
enabled: Boolean(options.enabled),
|
|
47
|
+
builtIns: Object.fromEntries(BUILT_IN_NAMES.map((name) => [name, true])),
|
|
48
|
+
patterns: [],
|
|
49
|
+
envVars: [],
|
|
50
|
+
allowlistRepos: []
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
function parseRedactionYaml(text) {
|
|
55
|
+
const config = defaultRedactionConfig({ enabled: true });
|
|
33
56
|
const patterns = [];
|
|
34
57
|
const envVars = [];
|
|
35
58
|
const allowlistRepos = [];
|
|
59
|
+
const builtIns = {};
|
|
36
60
|
const lines = text.split(/\r?\n/);
|
|
37
61
|
let section = "";
|
|
38
62
|
let currentPattern = null;
|
|
@@ -46,10 +70,15 @@ function parseRedactionYaml(text) {
|
|
|
46
70
|
if (top && !line.startsWith(" ")) {
|
|
47
71
|
section = top[1];
|
|
48
72
|
const inline = top[2].trim();
|
|
49
|
-
if (section === "
|
|
73
|
+
if (section === "enabled") {
|
|
74
|
+
config.enabled = parseYamlBoolean(inline, config.enabled);
|
|
75
|
+
} else if (section === "env_vars" && inline.startsWith("[")) {
|
|
50
76
|
envVars.push(...parseInlineList(inline));
|
|
51
77
|
} else if (section === "allowlist_repos" && inline.startsWith("[")) {
|
|
52
78
|
allowlistRepos.push(...parseInlineList(inline));
|
|
79
|
+
} else if (section === "built_ins" && inline.startsWith("[")) {
|
|
80
|
+
const enabled = new Set(parseInlineList(inline));
|
|
81
|
+
for (const name of BUILT_IN_NAMES) builtIns[name] = enabled.has(name);
|
|
53
82
|
}
|
|
54
83
|
continue;
|
|
55
84
|
}
|
|
@@ -71,16 +100,28 @@ function parseRedactionYaml(text) {
|
|
|
71
100
|
} else if (section === "allowlist_repos") {
|
|
72
101
|
const item = trimmed.match(/^-\s*(.+)$/);
|
|
73
102
|
if (item) allowlistRepos.push(stripYamlString(item[1].trim()).toLowerCase());
|
|
103
|
+
} else if (section === "built_ins") {
|
|
104
|
+
const pair = trimmed.match(/^([A-Za-z0-9_.:-]+)\s*:\s*(.+)$/);
|
|
105
|
+
if (pair) builtIns[pair[1].trim()] = parseYamlBoolean(pair[2].trim(), true);
|
|
74
106
|
}
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
return {
|
|
110
|
+
enabled: config.enabled,
|
|
111
|
+
builtIns: { ...config.builtIns, ...builtIns },
|
|
78
112
|
patterns: patterns.filter((pattern) => pattern.name && pattern.regex),
|
|
79
113
|
envVars: [...new Set(envVars.filter(Boolean))],
|
|
80
114
|
allowlistRepos: [...new Set(allowlistRepos.filter(Boolean))]
|
|
81
115
|
};
|
|
82
116
|
}
|
|
83
117
|
|
|
118
|
+
function parseYamlBoolean(value, fallback) {
|
|
119
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
120
|
+
if (["true", "yes", "on", "1"].includes(normalized)) return true;
|
|
121
|
+
if (["false", "no", "off", "0"].includes(normalized)) return false;
|
|
122
|
+
return fallback;
|
|
123
|
+
}
|
|
124
|
+
|
|
84
125
|
function parseInlineList(value) {
|
|
85
126
|
return value
|
|
86
127
|
.replace(/^\[/, "")
|
|
@@ -135,33 +176,38 @@ function redactText(input, options = {}) {
|
|
|
135
176
|
const repo = options.repoCanonical || "";
|
|
136
177
|
const cfg = options.config || loadRedactionConfig(options.env);
|
|
137
178
|
|
|
179
|
+
if (!cfg.enabled) return { text, summary };
|
|
180
|
+
|
|
138
181
|
if (repo && cfg.allowlistRepos.includes(repo.toLowerCase())) {
|
|
139
182
|
return { text, summary };
|
|
140
183
|
}
|
|
141
184
|
|
|
142
185
|
for (const pattern of BUILT_INS) {
|
|
143
|
-
|
|
186
|
+
if (!redactionBuiltInEnabled(cfg, pattern.name)) continue;
|
|
187
|
+
text = replaceMatches(text, pattern.regex, redactionMarker(pattern.name), summary, pattern.name);
|
|
144
188
|
}
|
|
145
189
|
|
|
146
190
|
const envValues = options.envValues || loadEnvValues(options.cwd, cfg.envVars, options.env);
|
|
147
191
|
for (const item of envValues) {
|
|
148
192
|
const escaped = escapeRegExp(item.value);
|
|
149
193
|
if (!escaped) continue;
|
|
150
|
-
text = replaceMatches(text, new RegExp(escaped, "g"), `
|
|
194
|
+
text = replaceMatches(text, new RegExp(escaped, "g"), redactionMarker(`env:${item.name}`), summary, `env:${item.name}`);
|
|
151
195
|
}
|
|
152
196
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
197
|
+
if (redactionBuiltInEnabled(cfg, HIGH_ENTROPY_ENV_BUILT_IN)) {
|
|
198
|
+
text = text.replace(
|
|
199
|
+
/(\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PWD)[A-Z0-9_]*\s*=\s*)(["']?)([A-Za-z0-9+/=_-]{32,})(\2)/gi,
|
|
200
|
+
(_, prefix, quote, value, suffix) => {
|
|
201
|
+
if (!looksHighEntropy(value)) return `${prefix}${quote}${value}${suffix}`;
|
|
202
|
+
increment(summary, HIGH_ENTROPY_ENV_BUILT_IN);
|
|
203
|
+
return `${prefix}${quote}${redactionMarker(HIGH_ENTROPY_ENV_BUILT_IN)}${suffix}`;
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}
|
|
161
207
|
|
|
162
208
|
for (const pattern of cfg.patterns) {
|
|
163
209
|
try {
|
|
164
|
-
text = replaceMatches(text, new RegExp(pattern.regex, "g"),
|
|
210
|
+
text = replaceMatches(text, new RegExp(pattern.regex, "g"), redactionMarker(pattern.name), summary, pattern.name);
|
|
165
211
|
} catch {
|
|
166
212
|
increment(summary, "invalid_user_pattern");
|
|
167
213
|
}
|
|
@@ -170,15 +216,85 @@ function redactText(input, options = {}) {
|
|
|
170
216
|
return { text, summary };
|
|
171
217
|
}
|
|
172
218
|
|
|
219
|
+
function redactionBuiltInEnabled(config, name) {
|
|
220
|
+
if (!BUILT_IN_NAMES.includes(name)) return false;
|
|
221
|
+
if (!config.builtIns || !(name in config.builtIns)) return true;
|
|
222
|
+
return config.builtIns[name] !== false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function redactionBuiltInNames() {
|
|
226
|
+
return [...BUILT_IN_NAMES];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function redactionConfigTemplate(config = defaultRedactionConfig({ enabled: true })) {
|
|
230
|
+
const cfg = {
|
|
231
|
+
...defaultRedactionConfig({ enabled: true }),
|
|
232
|
+
...(config || {}),
|
|
233
|
+
builtIns: { ...defaultRedactionConfig({ enabled: true }).builtIns, ...((config && config.builtIns) || {}) }
|
|
234
|
+
};
|
|
235
|
+
const lines = [
|
|
236
|
+
"# Agentlog redaction is opt-in. Set enabled: true to redact future imports.",
|
|
237
|
+
`enabled: ${cfg.enabled ? "true" : "false"}`,
|
|
238
|
+
"",
|
|
239
|
+
"built_ins:"
|
|
240
|
+
];
|
|
241
|
+
for (const name of BUILT_IN_NAMES) lines.push(` ${name}: ${redactionBuiltInEnabled(cfg, name) ? "true" : "false"}`);
|
|
242
|
+
lines.push("", "env_vars:");
|
|
243
|
+
if (cfg.envVars?.length) {
|
|
244
|
+
for (const name of cfg.envVars) lines.push(` - ${formatYamlString(name)}`);
|
|
245
|
+
} else {
|
|
246
|
+
lines[lines.length - 1] = "env_vars: []";
|
|
247
|
+
}
|
|
248
|
+
lines.push("", "patterns:");
|
|
249
|
+
if (cfg.patterns?.length) {
|
|
250
|
+
for (const pattern of cfg.patterns) {
|
|
251
|
+
lines.push(` - name: ${formatYamlString(pattern.name)}`);
|
|
252
|
+
lines.push(` regex: ${formatYamlString(pattern.regex)}`);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
lines[lines.length - 1] = "patterns: []";
|
|
256
|
+
}
|
|
257
|
+
lines.push("", "allowlist_repos:");
|
|
258
|
+
if (cfg.allowlistRepos?.length) {
|
|
259
|
+
for (const repo of cfg.allowlistRepos) lines.push(` - ${formatYamlString(repo)}`);
|
|
260
|
+
} else {
|
|
261
|
+
lines[lines.length - 1] = "allowlist_repos: []";
|
|
262
|
+
}
|
|
263
|
+
lines.push("");
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function formatYamlString(value) {
|
|
268
|
+
return JSON.stringify(String(value || ""));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function saveRedactionConfig(config, env = process.env) {
|
|
272
|
+
const file = paths(env).redaction;
|
|
273
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
274
|
+
fs.writeFileSync(file, redactionConfigTemplate(config), { mode: 0o600 });
|
|
275
|
+
return file;
|
|
276
|
+
}
|
|
277
|
+
|
|
173
278
|
function styleRedactionMarkersForMarkdown(input) {
|
|
174
279
|
return String(input ?? "").replace(REDACTION_MARKER_RE, (_, rawKind) => {
|
|
175
|
-
const
|
|
176
|
-
return `<mark class="agentlog-redaction" title="agentlog redacted ${escapeHtml(
|
|
280
|
+
const label = redactionLabel(rawKind);
|
|
281
|
+
return `<mark class="agentlog-redaction" title="agentlog redacted ${escapeHtml(label)}">[REDACTED ${escapeHtml(label)}]</mark>`;
|
|
177
282
|
});
|
|
178
283
|
}
|
|
179
284
|
|
|
285
|
+
function redactionMarker(kind) {
|
|
286
|
+
return `[REDACTED ${redactionLabel(kind)}]`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function redactionLabel(kind) {
|
|
290
|
+
const safeKind = safeRedactionKind(kind);
|
|
291
|
+
if (REDACTION_LABELS[safeKind]) return REDACTION_LABELS[safeKind];
|
|
292
|
+
if (safeKind.startsWith("env:")) return `ENV ${safeKind.slice(4).replace(/[^A-Za-z0-9_.-]+/g, "_").toUpperCase()}`;
|
|
293
|
+
return safeKind.replace(/[_:.-]+/g, " ").replace(/\s+/g, " ").trim().toUpperCase() || "UNKNOWN";
|
|
294
|
+
}
|
|
295
|
+
|
|
180
296
|
function safeRedactionKind(value) {
|
|
181
|
-
return String(value || "unknown").replace(/[^\w
|
|
297
|
+
return String(value || "unknown").trim().replace(/[^\w:.\s-]/g, "_").replace(/\s+/g, "_").toLowerCase().slice(0, 120) || "unknown";
|
|
182
298
|
}
|
|
183
299
|
|
|
184
300
|
function escapeHtml(value) {
|
|
@@ -219,10 +335,17 @@ function escapeRegExp(value) {
|
|
|
219
335
|
}
|
|
220
336
|
|
|
221
337
|
module.exports = {
|
|
338
|
+
defaultRedactionConfig,
|
|
222
339
|
loadEnvValues,
|
|
223
340
|
loadRedactionConfig,
|
|
224
341
|
mergeSummaries,
|
|
225
342
|
parseRedactionYaml,
|
|
343
|
+
redactionBuiltInEnabled,
|
|
344
|
+
redactionBuiltInNames,
|
|
345
|
+
redactionConfigTemplate,
|
|
346
|
+
redactionLabel,
|
|
347
|
+
redactionMarker,
|
|
226
348
|
redactText,
|
|
349
|
+
saveRedactionConfig,
|
|
227
350
|
styleRedactionMarkersForMarkdown
|
|
228
351
|
};
|