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/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
  );
@@ -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 PARSER_VERSIONS = {
9
- "codex-cli-history": "1.0.0",
10
- "codex-desktop-history": "1.0.0",
11
- "cli-history": "1.0.0",
12
- "claude-sdk-history": "1.0.0",
13
- "claude-code-desktop-metadata": "1.0.0",
14
- "claude-workspace-desktop": "1.0.0",
15
- "cursor-workspace-sqlite": "1.0.0",
16
- "cursor-global-sqlite": "1.0.0",
17
- "cursor-raw-sqlite-salvage": "1.0.0",
18
- "cursor-agent-transcripts": "1.0.0",
19
- "devin-cli-history": "1.0.0",
20
- "gemini-cli-history": "1.0.0",
21
- "cline-task-history": "1.0.0",
22
- "opencode-history": "1.0.0",
23
- "aider-chat-history": "1.0.0",
24
- "antigravity-history": "1.0.0",
25
- "web-chat-export": "1.0.0",
26
- "chatgpt-export": "1.0.0",
27
- "claude-web-export": "1.0.0",
28
- "claude-web-memory": "1.0.0",
29
- import: "1.0.0"
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
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
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:([^\]\n]+)\]/g;
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 { patterns: [], envVars: [], allowlistRepos: [] };
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 === "env_vars" && inline.startsWith("[")) {
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
- text = replaceMatches(text, pattern.regex, `[REDACTED:${pattern.name}]`, summary, pattern.name);
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"), `[REDACTED:env:${item.name}]`, summary, `env:${item.name}`);
194
+ text = replaceMatches(text, new RegExp(escaped, "g"), redactionMarker(`env:${item.name}`), summary, `env:${item.name}`);
151
195
  }
152
196
 
153
- text = text.replace(
154
- /(\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PWD)[A-Z0-9_]*\s*=\s*)(["']?)([A-Za-z0-9+/=_-]{32,})(\2)/gi,
155
- (_, prefix, quote, value, suffix) => {
156
- if (!looksHighEntropy(value)) return `${prefix}${quote}${value}${suffix}`;
157
- increment(summary, "high_entropy_env");
158
- return `${prefix}${quote}[REDACTED:high_entropy_env]${suffix}`;
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"), `[REDACTED:${pattern.name}]`, summary, pattern.name);
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 kind = safeRedactionKind(rawKind);
176
- return `<mark class="agentlog-redaction" title="agentlog redacted ${escapeHtml(kind)}">[REDACTED:${escapeHtml(kind)}]</mark>`;
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:.-]/g, "_").slice(0, 120) || "unknown";
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
  };