agentel 0.2.8 → 0.3.0

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.
@@ -0,0 +1,422 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+
5
+ const PROVIDER = "pi";
6
+
7
+ function parsePiSessionFile(file, options = {}) {
8
+ let text;
9
+ try {
10
+ text = fs.readFileSync(file, "utf8");
11
+ } catch {
12
+ return null;
13
+ }
14
+ return parsePiSessionText(text, options);
15
+ }
16
+
17
+ // pi sessions are JSONL with a `session` header line followed by tree-linked
18
+ // entries (id/parentId, 8-char hex). v1 files have no entry ids and store
19
+ // provider/modelId on the header; v2 added the tree plus branch/label/custom
20
+ // entries; v3 renamed the hookMessage role to custom. Entries are imported in
21
+ // file order (branches included) with ids preserved in metadata.
22
+ function parsePiSessionText(text, options = {}) {
23
+ const lines = String(text || "").split(/\r?\n/);
24
+ let header = null;
25
+ let name = "";
26
+ const messages = [];
27
+ const fallbackTime = toIso(options.fallbackTime) || new Date().toISOString();
28
+ let index = 0;
29
+ for (const line of lines) {
30
+ if (!line.trim()) continue;
31
+ const entry = parseJson(line);
32
+ if (!entry || typeof entry !== "object") continue;
33
+ if (entry.type === "session") {
34
+ header ||= entry;
35
+ continue;
36
+ }
37
+ if (!header) return null;
38
+ const timestamp = toIso(entry.timestamp) || offsetTimestamp(fallbackTime, index);
39
+ index++;
40
+ if (entry.type === "session_info") {
41
+ name = firstString(entry.name, name);
42
+ continue;
43
+ }
44
+ if (entry.type === "message" && entry.message && typeof entry.message === "object") {
45
+ messages.push(...piEntryMessages(entry, timestamp, header));
46
+ continue;
47
+ }
48
+ const systemMessage = piSystemMessage(entry, timestamp);
49
+ if (systemMessage) messages.push(systemMessage);
50
+ }
51
+ if (!header || !firstString(header.id)) return null;
52
+ return {
53
+ header,
54
+ sessionId: firstString(header.id),
55
+ title: name,
56
+ cwd: firstString(header.cwd),
57
+ version: Number(header.version) || 1,
58
+ parentSession: firstString(header.parentSession, header.branchedFrom),
59
+ messages,
60
+ startedAt: toIso(header.timestamp) || messages[0]?.timestamp || fallbackTime,
61
+ endedAt: messages[messages.length - 1]?.timestamp || toIso(header.timestamp) || fallbackTime
62
+ };
63
+ }
64
+
65
+ function piEntryMessages(entry, timestamp, header) {
66
+ const payload = entry.message;
67
+ const role = String(payload.role || "");
68
+ const base = compactObject({
69
+ provider: PROVIDER,
70
+ providerMessageId: firstString(entry.id),
71
+ parentMessageId: firstString(entry.parentId)
72
+ }) || { provider: PROVIDER };
73
+ if (role === "user") {
74
+ const content = piContentText(payload.content);
75
+ if (!content) return [];
76
+ return [{ role: "user", content, timestamp, metadata: { ...base } }];
77
+ }
78
+ if (role === "assistant") {
79
+ return piAssistantMessages(payload, timestamp, base, header);
80
+ }
81
+ if (role === "toolResult") {
82
+ const toolResult = piToolResult(payload);
83
+ if (!toolResult) return [];
84
+ return [{ role: "tool", content: toolResult.output, timestamp, metadata: { ...base, toolResult } }];
85
+ }
86
+ if (role === "bashExecution") {
87
+ return piBashExecutionMessages(payload, timestamp, base);
88
+ }
89
+ if (role === "custom" || role === "hookMessage") {
90
+ const content = piContentText(payload.content);
91
+ if (!content) return [];
92
+ return [{
93
+ role: "system",
94
+ content,
95
+ timestamp,
96
+ metadata: { ...base, providerGenerated: true, contextKind: "extension", contextSource: firstString(payload.customType) || undefined }
97
+ }];
98
+ }
99
+ if (role === "branchSummary" || role === "compactionSummary") {
100
+ const content = firstString(payload.summary);
101
+ if (!content) return [];
102
+ return [{
103
+ role: "system",
104
+ content,
105
+ timestamp,
106
+ metadata: { ...base, providerGenerated: true, contextKind: role === "branchSummary" ? "branch_summary" : "compaction" }
107
+ }];
108
+ }
109
+ return [];
110
+ }
111
+
112
+ function piAssistantMessages(payload, timestamp, base, header) {
113
+ const blocks = Array.isArray(payload.content) ? payload.content : [];
114
+ const texts = [];
115
+ const thinking = [];
116
+ const toolCalls = [];
117
+ for (const block of blocks) {
118
+ if (!block || typeof block !== "object") continue;
119
+ if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
120
+ else if (block.type === "thinking") thinking.push(block.redacted ? "[redacted thinking]" : firstString(block.thinking));
121
+ else if (block.type === "toolCall") {
122
+ const call = piToolCall(block);
123
+ if (call) toolCalls.push(call);
124
+ }
125
+ }
126
+ if (typeof payload.content === "string") texts.push(payload.content);
127
+ const content = texts.filter(Boolean).join("\n").trim();
128
+ const thinkingText = thinking.filter(Boolean).join("\n\n").trim();
129
+ if (!content && !toolCalls.length && !thinkingText && !payload.errorMessage) return [];
130
+ return [{
131
+ role: "assistant",
132
+ content: content || (payload.errorMessage ? `Error: ${payload.errorMessage}` : ""),
133
+ timestamp,
134
+ metadata: compactObject({
135
+ ...base,
136
+ model: firstString(payload.model, payload.responseModel, header?.modelId) || undefined,
137
+ modelProvider: firstString(payload.provider, header?.provider) || undefined,
138
+ thinking: thinkingText || undefined,
139
+ stopReason: firstString(payload.stopReason) || undefined,
140
+ isError: payload.stopReason === "error" || undefined,
141
+ usage: piUsage(payload.usage),
142
+ toolCalls: toolCalls.length ? toolCalls : undefined
143
+ })
144
+ }];
145
+ }
146
+
147
+ function piToolCall(block) {
148
+ const name = firstString(block.name);
149
+ if (!name) return null;
150
+ const args = block.arguments && typeof block.arguments === "object" ? block.arguments : undefined;
151
+ const summary = summarizeToolArguments(args);
152
+ return compactObject({
153
+ id: firstString(block.id) || undefined,
154
+ name,
155
+ displayName: toolDisplayName(name),
156
+ rawCategory: "toolCall",
157
+ category: piToolCategory(name),
158
+ title: toolDisplayName(name),
159
+ status: "tool_call",
160
+ argument: summary,
161
+ rawInputSummary: summary,
162
+ inputPreview: summary,
163
+ target: toolTarget(args) || undefined,
164
+ arguments: args,
165
+ provider: PROVIDER
166
+ });
167
+ }
168
+
169
+ function piToolResult(payload) {
170
+ const output = piContentText(payload.content);
171
+ const id = firstString(payload.toolCallId);
172
+ if (!output && !id) return null;
173
+ const isError = payload.isError === true;
174
+ const lineCount = output ? output.split(/\r?\n/).length : 0;
175
+ return compactObject({
176
+ provider: PROVIDER,
177
+ id: id || undefined,
178
+ kind: toolDisplayName(firstString(payload.toolName, "tool")),
179
+ title: isError ? "Tool error" : "Tool result",
180
+ rawCategory: "toolResult",
181
+ category: piToolCategory(firstString(payload.toolName)),
182
+ summary: firstLine(output),
183
+ output,
184
+ lineCount: lineCount || undefined,
185
+ collapsed: lineCount > 18 || undefined,
186
+ status: isError ? "error" : "completed"
187
+ });
188
+ }
189
+
190
+ // `!` shell commands typed by the user are stored as bashExecution messages
191
+ // with the captured output and exit code.
192
+ function piBashExecutionMessages(payload, timestamp, base) {
193
+ const command = firstString(payload.command);
194
+ if (!command) return [];
195
+ const messages = [{
196
+ role: "user",
197
+ content: `! ${command}`,
198
+ timestamp,
199
+ metadata: { ...base, eventType: "pi-bash-execution" }
200
+ }];
201
+ const output = firstString(payload.output);
202
+ if (output) {
203
+ const lineCount = output.split(/\r?\n/).length;
204
+ messages.push({
205
+ role: "tool",
206
+ content: output,
207
+ timestamp,
208
+ metadata: {
209
+ ...base,
210
+ toolResult: compactObject({
211
+ provider: PROVIDER,
212
+ kind: "Bash",
213
+ title: payload.exitCode ? "Tool error" : "Tool result",
214
+ rawCategory: "bashExecution",
215
+ category: "shell",
216
+ summary: firstLine(output),
217
+ output,
218
+ lineCount,
219
+ collapsed: lineCount > 18 || undefined,
220
+ status: payload.cancelled ? "cancelled" : payload.exitCode ? "error" : "completed",
221
+ exitCode: Number.isFinite(Number(payload.exitCode)) ? Number(payload.exitCode) : undefined
222
+ })
223
+ }
224
+ });
225
+ }
226
+ return messages;
227
+ }
228
+
229
+ function piSystemMessage(entry, timestamp) {
230
+ const base = compactObject({
231
+ provider: PROVIDER,
232
+ providerMessageId: firstString(entry.id),
233
+ parentMessageId: firstString(entry.parentId),
234
+ providerGenerated: true
235
+ });
236
+ if (entry.type === "model_change") {
237
+ const model = firstString(entry.modelId, entry.model);
238
+ if (!model) return null;
239
+ return {
240
+ role: "system",
241
+ content: `Model changed to ${model}`,
242
+ timestamp,
243
+ metadata: { ...base, eventType: "pi-model-change", contextKind: "model_change", model }
244
+ };
245
+ }
246
+ if (entry.type === "thinking_level_change") {
247
+ const level = firstString(entry.thinkingLevel);
248
+ if (!level) return null;
249
+ return {
250
+ role: "system",
251
+ content: `Thinking level changed to ${level}`,
252
+ timestamp,
253
+ metadata: { ...base, eventType: "pi-thinking-level-change", contextKind: "thinking_level_change" }
254
+ };
255
+ }
256
+ if (entry.type === "compaction") {
257
+ return {
258
+ role: "system",
259
+ content: firstString(entry.summary, "Context compacted"),
260
+ timestamp,
261
+ metadata: compactObject({
262
+ ...base,
263
+ eventType: "pi-compaction",
264
+ contextKind: "compaction",
265
+ tokensBefore: Number.isFinite(Number(entry.tokensBefore)) ? Number(entry.tokensBefore) : undefined,
266
+ firstKeptEntryId: firstString(entry.firstKeptEntryId) || undefined
267
+ })
268
+ };
269
+ }
270
+ if (entry.type === "branch_summary") {
271
+ return {
272
+ role: "system",
273
+ content: firstString(entry.summary, "Returned from branch"),
274
+ timestamp,
275
+ metadata: compactObject({ ...base, eventType: "pi-branch-summary", contextKind: "branch_summary", fromId: firstString(entry.fromId) || undefined })
276
+ };
277
+ }
278
+ if (entry.type === "custom_message") {
279
+ const content = piContentText(entry.content);
280
+ if (!content) return null;
281
+ return {
282
+ role: "system",
283
+ content,
284
+ timestamp,
285
+ metadata: compactObject({ ...base, eventType: "pi-custom-message", contextKind: "extension", contextSource: firstString(entry.customType) || undefined })
286
+ };
287
+ }
288
+ return null;
289
+ }
290
+
291
+ // pi usage mirrors API token fields: input excludes cache reads/writes, and
292
+ // per-message dollar cost is stored under cost.total.
293
+ function piUsage(usage) {
294
+ if (!usage || typeof usage !== "object") return undefined;
295
+ const inputTokens = positiveNumber(usage.input);
296
+ const outputTokens = positiveNumber(usage.output);
297
+ const cacheReadTokens = positiveNumber(usage.cacheRead);
298
+ const cacheCreationInputTokens = positiveNumber(usage.cacheWrite);
299
+ const totalTokens = positiveNumber(usage.totalTokens);
300
+ const costUsd = positiveNumber(usage.cost?.total);
301
+ if (!inputTokens && !outputTokens && !cacheReadTokens && !cacheCreationInputTokens && !totalTokens) return undefined;
302
+ return compactObject({
303
+ inputTokens,
304
+ outputTokens,
305
+ cacheReadTokens,
306
+ cacheCreationInputTokens,
307
+ totalTokens,
308
+ costUsd
309
+ });
310
+ }
311
+
312
+ function piContentText(content) {
313
+ if (typeof content === "string") return content.trim();
314
+ if (!Array.isArray(content)) return "";
315
+ return content
316
+ .map((block) => {
317
+ if (typeof block === "string") return block;
318
+ if (block && typeof block === "object" && typeof block.text === "string") return block.text;
319
+ if (block && typeof block === "object" && block.type === "image") return "[Attachment: image]";
320
+ return "";
321
+ })
322
+ .filter(Boolean)
323
+ .join("\n")
324
+ .trim();
325
+ }
326
+
327
+ function piToolCategory(name) {
328
+ const text = String(name || "");
329
+ if (/subagent|task|agent/i.test(text)) return "task";
330
+ if (/bash|shell|command|exec/i.test(text)) return "shell";
331
+ if (/web|fetch|url|http|browser/i.test(text)) return "web";
332
+ if (/edit|write|patch|replace/i.test(text)) return "edit";
333
+ if (/read|view|cat|ls|list/i.test(text)) return "read";
334
+ if (/grep|glob|find|search|rg/i.test(text)) return "search";
335
+ if (/^mcp/i.test(text)) return "mcp";
336
+ return "tool";
337
+ }
338
+
339
+ function summarizeToolArguments(value) {
340
+ if (value == null) return "";
341
+ if (typeof value === "string") return value.slice(0, 240);
342
+ if (typeof value !== "object") return String(value).slice(0, 240);
343
+ for (const key of ["command", "prompt", "query", "pattern", "path", "file_path", "url"]) {
344
+ if (value[key]) return String(value[key]).slice(0, 240);
345
+ }
346
+ return Object.entries(value)
347
+ .slice(0, 3)
348
+ .map(([key, item]) => `${key}: ${typeof item === "string" ? item : JSON.stringify(item)}`)
349
+ .join(", ")
350
+ .slice(0, 240);
351
+ }
352
+
353
+ function toolTarget(args) {
354
+ if (!args || typeof args !== "object") return "";
355
+ return firstString(args.path, args.file_path, args.filePath, args.file, args.directory, args.url);
356
+ }
357
+
358
+ function toolDisplayName(value) {
359
+ const text = String(value || "tool").trim();
360
+ return text
361
+ .split(/_+|(?=[A-Z])/g)
362
+ .filter(Boolean)
363
+ .map((part) => part.replace(/(^|[-\s])([a-z])/g, (_, prefix, char) => `${prefix}${char.toUpperCase()}`))
364
+ .join(" ");
365
+ }
366
+
367
+ function firstLine(value) {
368
+ return String(value || "").split(/\r?\n/).find((line) => line.trim())?.trim() || "";
369
+ }
370
+
371
+ function offsetTimestamp(base, index) {
372
+ const date = new Date(base);
373
+ if (Number.isNaN(date.valueOf())) return base;
374
+ return new Date(date.valueOf() + index * 1000).toISOString();
375
+ }
376
+
377
+ function toIso(value) {
378
+ if (!value) return "";
379
+ if (typeof value === "number") {
380
+ const ms = value > 1e12 ? value : value * 1000;
381
+ const date = new Date(ms);
382
+ return Number.isNaN(date.valueOf()) ? "" : date.toISOString();
383
+ }
384
+ const date = new Date(value);
385
+ return Number.isNaN(date.valueOf()) ? "" : date.toISOString();
386
+ }
387
+
388
+ function parseJson(text) {
389
+ if (typeof text !== "string" || !text.trim()) return null;
390
+ try {
391
+ return JSON.parse(text);
392
+ } catch {
393
+ return null;
394
+ }
395
+ }
396
+
397
+ function firstString(...values) {
398
+ for (const value of values) {
399
+ if (typeof value === "string" && value.trim()) return value.trim();
400
+ }
401
+ return "";
402
+ }
403
+
404
+ function positiveNumber(value) {
405
+ const number = Number(value);
406
+ return Number.isFinite(number) && number > 0 ? number : undefined;
407
+ }
408
+
409
+ function compactObject(value) {
410
+ if (!value || typeof value !== "object") return value;
411
+ const result = {};
412
+ for (const [key, item] of Object.entries(value)) {
413
+ if (item === undefined || item === null || item === "") continue;
414
+ result[key] = item;
415
+ }
416
+ return Object.keys(result).length ? result : undefined;
417
+ }
418
+
419
+ module.exports = {
420
+ parsePiSessionFile,
421
+ parsePiSessionText
422
+ };
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const { canonicalImportSource } = require("../sources");
4
+
3
5
  const PROVIDER_ADAPTERS = [
4
6
  {
5
7
  source: "codex",
@@ -67,10 +69,10 @@ const PROVIDER_ADAPTERS = [
67
69
  helpers.importClaudeDesktopProvider("claude_desktop", since, { ...options, claudeDesktopKind: "claude-code-desktop-metadata" }, env)
68
70
  },
69
71
  {
70
- source: "claude-workspace",
72
+ source: "claude-cowork",
71
73
  provider: "claude_desktop",
72
74
  sourceType: "claude-workspace-desktop",
73
- label: "Claude Workspace",
75
+ label: "Claude Cowork",
74
76
  run: ({ helpers, since, options, env }) =>
75
77
  helpers.importClaudeDesktopProvider("claude_desktop", since, { ...options, claudeDesktopKind: "claude-workspace-desktop" }, env)
76
78
  },
@@ -146,9 +148,10 @@ const PROVIDER_ADAPTERS = [
146
148
  {
147
149
  source: "windsurf",
148
150
  provider: "windsurf",
151
+ sourceType: "windsurf-cascade-brain",
149
152
  label: "Windsurf",
150
- disabled: true,
151
- run: ({ helpers }) => helpers.disabledImportResult("windsurf")
153
+ run: ({ helpers, since, options, env }) =>
154
+ helpers.importStructuredProvider("windsurf", helpers.readWindsurfSessions(options, env), since, options, env)
152
155
  },
153
156
  {
154
157
  source: "antigravity",
@@ -158,6 +161,22 @@ const PROVIDER_ADAPTERS = [
158
161
  run: ({ helpers, since, options, env }) =>
159
162
  helpers.importStructuredProvider("antigravity", helpers.readAntigravitySessions(options, env), since, options, env)
160
163
  },
164
+ {
165
+ source: "antigravity-cli",
166
+ provider: "antigravity_cli",
167
+ sourceType: "antigravity-cli-transcript-log",
168
+ label: "Antigravity CLI",
169
+ run: ({ helpers, since, options, env }) =>
170
+ helpers.importStructuredProvider("antigravity_cli", helpers.readAntigravityCliSessions(options, env), since, options, env)
171
+ },
172
+ {
173
+ source: "antigravity-ide",
174
+ provider: "antigravity_ide",
175
+ sourceType: "antigravity-ide-transcript-log",
176
+ label: "Antigravity IDE",
177
+ run: ({ helpers, since, options, env }) =>
178
+ helpers.importStructuredProvider("antigravity_ide", helpers.readAntigravityIdeSessions(options, env), since, options, env)
179
+ },
161
180
  {
162
181
  source: "devin-cli",
163
182
  provider: "devin",
@@ -165,11 +184,51 @@ const PROVIDER_ADAPTERS = [
165
184
  label: "Devin CLI",
166
185
  run: ({ helpers, since, options, env }) =>
167
186
  helpers.importStructuredProvider("devin", helpers.readDevinSessions(env, options), since, options, env)
187
+ },
188
+ {
189
+ source: "devin-desktop",
190
+ provider: "devin",
191
+ sourceType: "devin-desktop-acp-events",
192
+ label: "Devin Desktop",
193
+ run: ({ helpers, since, options, env }) =>
194
+ helpers.importStructuredProvider("devin", helpers.readDevinDesktopSessions(env, options), since, options, env)
195
+ },
196
+ {
197
+ source: "copilot-cli",
198
+ provider: "copilot",
199
+ sourceType: "copilot-cli-history",
200
+ label: "GitHub Copilot CLI",
201
+ run: ({ helpers, since, options, env }) =>
202
+ helpers.importStructuredProvider("copilot", helpers.readCopilotCliSessions(env, options), since, options, env)
203
+ },
204
+ {
205
+ source: "factory",
206
+ provider: "factory",
207
+ sourceType: "factory-droid-history",
208
+ label: "Factory Droid",
209
+ run: ({ helpers, since, options, env }) =>
210
+ helpers.importStructuredProvider("factory", helpers.readFactorySessions(env, options), since, options, env)
211
+ },
212
+ {
213
+ source: "grok-build",
214
+ provider: "grok",
215
+ sourceType: "grok-build-history",
216
+ label: "Grok Build",
217
+ run: ({ helpers, since, options, env }) =>
218
+ helpers.importStructuredProvider("grok", helpers.readGrokBuildSessions(env, options), since, options, env)
219
+ },
220
+ {
221
+ source: "pi",
222
+ provider: "pi",
223
+ sourceType: "pi-cli-history",
224
+ label: "pi",
225
+ run: ({ helpers, since, options, env }) =>
226
+ helpers.importStructuredProvider("pi", helpers.readPiSessions(env, options), since, options, env)
168
227
  }
169
228
  ];
170
229
 
171
230
  function providerAdapterForSource(source) {
172
- const key = String(source || "").trim();
231
+ const key = canonicalImportSource(source);
173
232
  return PROVIDER_ADAPTERS.find((adapter) => adapter.source === key) || null;
174
233
  }
175
234