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
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
const PROVIDER = "factory";
|
|
6
|
+
|
|
7
|
+
function parseFactorySessionFile(file, options = {}) {
|
|
8
|
+
let text;
|
|
9
|
+
try {
|
|
10
|
+
text = fs.readFileSync(file, "utf8");
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return parseFactorySessionText(text, options);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Factory droid transcripts are JSONL: a `session_start` header line followed
|
|
18
|
+
// by `message` wrapper lines whose payloads use Anthropic-style content blocks.
|
|
19
|
+
// Block keys drift between snake_case and camelCase across droid versions
|
|
20
|
+
// (tool_use_id vs toolUseId, media_type vs mediaType), so accept both.
|
|
21
|
+
function parseFactorySessionText(text, options = {}) {
|
|
22
|
+
const lines = String(text || "").split(/\r?\n/);
|
|
23
|
+
let header = null;
|
|
24
|
+
const messages = [];
|
|
25
|
+
const fallbackTime = toIso(options.fallbackTime) || new Date().toISOString();
|
|
26
|
+
let index = 0;
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
if (!line.trim()) continue;
|
|
29
|
+
const entry = parseJson(line);
|
|
30
|
+
if (!entry || typeof entry !== "object") continue;
|
|
31
|
+
if (entry.type === "session_start") {
|
|
32
|
+
header ||= entry;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (!header) continue;
|
|
36
|
+
if (entry.type === "todo_state") continue;
|
|
37
|
+
if (entry.type !== "message" || !entry.message || typeof entry.message !== "object") continue;
|
|
38
|
+
messages.push(...factoryEntryMessages(entry, { fallbackTime, index }));
|
|
39
|
+
index++;
|
|
40
|
+
}
|
|
41
|
+
if (!header || !firstString(header.id)) return null;
|
|
42
|
+
return {
|
|
43
|
+
header,
|
|
44
|
+
sessionId: firstString(header.id),
|
|
45
|
+
title: firstString(header.sessionTitle, header.title),
|
|
46
|
+
cwd: firstString(header.cwd, header.lastCwd),
|
|
47
|
+
messages,
|
|
48
|
+
startedAt: messages[0]?.timestamp || fallbackTime,
|
|
49
|
+
endedAt: messages[messages.length - 1]?.timestamp || fallbackTime
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function factoryEntryMessages(entry, context) {
|
|
54
|
+
const payload = entry.message;
|
|
55
|
+
const role = normalizeFactoryRole(payload.role);
|
|
56
|
+
const timestamp =
|
|
57
|
+
toIso(entry.timestamp) ||
|
|
58
|
+
toIso(payload.createdAt) ||
|
|
59
|
+
toIso(payload.timestamp) ||
|
|
60
|
+
offsetTimestamp(context.fallbackTime, context.index);
|
|
61
|
+
const blocks = normalizeFactoryContent(payload.content);
|
|
62
|
+
const texts = [];
|
|
63
|
+
const thinking = [];
|
|
64
|
+
const toolCalls = [];
|
|
65
|
+
const toolResults = [];
|
|
66
|
+
for (const block of blocks) {
|
|
67
|
+
if (!block || typeof block !== "object") continue;
|
|
68
|
+
const type = String(block.type || "");
|
|
69
|
+
if (type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
70
|
+
else if (type === "thinking") thinking.push(firstString(block.thinking, block.text));
|
|
71
|
+
else if (type === "redacted_thinking") thinking.push("[redacted thinking]");
|
|
72
|
+
else if (type === "tool_use") {
|
|
73
|
+
const call = factoryToolCall(block);
|
|
74
|
+
if (call) toolCalls.push(call);
|
|
75
|
+
} else if (type === "tool_result") {
|
|
76
|
+
const result = factoryToolResult(block);
|
|
77
|
+
if (result) toolResults.push(result);
|
|
78
|
+
} else if (type === "image" || type === "document") {
|
|
79
|
+
texts.push(factoryAttachmentLabel(block));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const result = [];
|
|
83
|
+
const content = texts.filter(Boolean).join("\n").trim();
|
|
84
|
+
const thinkingText = thinking.filter(Boolean).join("\n\n").trim();
|
|
85
|
+
if (content || toolCalls.length || thinkingText) {
|
|
86
|
+
result.push({
|
|
87
|
+
role: role === "tool" ? "assistant" : role,
|
|
88
|
+
content,
|
|
89
|
+
timestamp,
|
|
90
|
+
metadata: compactObject({
|
|
91
|
+
provider: PROVIDER,
|
|
92
|
+
providerMessageId: firstString(entry.id),
|
|
93
|
+
parentMessageId: firstString(entry.parentId),
|
|
94
|
+
model: role === "assistant" ? firstString(payload.model) : undefined,
|
|
95
|
+
thinking: thinkingText || undefined,
|
|
96
|
+
visibility: factoryVisibility(payload.visibility),
|
|
97
|
+
isError: payload.isError === true || undefined,
|
|
98
|
+
userMessageSource: firstString(payload.userMessageSource) || undefined,
|
|
99
|
+
toolCalls: toolCalls.length ? toolCalls : undefined
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
for (const toolResult of toolResults) {
|
|
104
|
+
result.push({
|
|
105
|
+
role: "tool",
|
|
106
|
+
content: toolResult.output,
|
|
107
|
+
timestamp,
|
|
108
|
+
metadata: compactObject({
|
|
109
|
+
provider: PROVIDER,
|
|
110
|
+
providerMessageId: firstString(entry.id),
|
|
111
|
+
toolResult
|
|
112
|
+
})
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function factoryToolCall(block) {
|
|
119
|
+
const name = firstString(block.name);
|
|
120
|
+
if (!name) return null;
|
|
121
|
+
const args = block.input && typeof block.input === "object" ? block.input : parseJson(block.input) || undefined;
|
|
122
|
+
const summary = summarizeToolArguments(args);
|
|
123
|
+
return compactObject({
|
|
124
|
+
id: firstString(block.id) || undefined,
|
|
125
|
+
name,
|
|
126
|
+
displayName: toolDisplayName(name),
|
|
127
|
+
rawCategory: "tool_use",
|
|
128
|
+
category: factoryToolCategory(name),
|
|
129
|
+
title: toolDisplayName(name),
|
|
130
|
+
status: "tool_call",
|
|
131
|
+
argument: summary,
|
|
132
|
+
rawInputSummary: summary,
|
|
133
|
+
inputPreview: summary,
|
|
134
|
+
target: toolTarget(args) || undefined,
|
|
135
|
+
arguments: args,
|
|
136
|
+
provider: PROVIDER
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function factoryToolResult(block) {
|
|
141
|
+
const id = firstString(block.tool_use_id, block.toolUseId);
|
|
142
|
+
const parsedContent = factoryToolResultContent(block.content);
|
|
143
|
+
const detail = parsedContent.detail;
|
|
144
|
+
const output = firstString(detail?.diff) || parsedContent.text || (detail ? JSON.stringify(detail) : "");
|
|
145
|
+
if (!output && !id) return null;
|
|
146
|
+
const isError = block.isError === true || block.is_error === true || detail?.success === false;
|
|
147
|
+
const lineCount = output ? output.split(/\r?\n/).length : 0;
|
|
148
|
+
const structuredPatch = factoryStructuredPatch(detail);
|
|
149
|
+
return compactObject({
|
|
150
|
+
provider: PROVIDER,
|
|
151
|
+
id: id || undefined,
|
|
152
|
+
kind: "Tool result",
|
|
153
|
+
title: isError ? "Tool error" : "Tool result",
|
|
154
|
+
rawCategory: "tool_result",
|
|
155
|
+
category: "tool",
|
|
156
|
+
summary: firstLine(output),
|
|
157
|
+
output,
|
|
158
|
+
lineCount: lineCount || undefined,
|
|
159
|
+
collapsed: lineCount > 18 || undefined,
|
|
160
|
+
status: isError ? "error" : "completed",
|
|
161
|
+
structuredPatch: structuredPatch.length ? structuredPatch : undefined
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function factoryToolResultContent(content) {
|
|
166
|
+
if (typeof content === "string") {
|
|
167
|
+
const detail = parseJson(content);
|
|
168
|
+
if (detail && typeof detail === "object" && !Array.isArray(detail) && (detail.diff || detail.diffLines || detail.success !== undefined)) {
|
|
169
|
+
return { text: firstString(detail.content, detail.message), detail };
|
|
170
|
+
}
|
|
171
|
+
return { text: content, detail: null };
|
|
172
|
+
}
|
|
173
|
+
if (Array.isArray(content)) {
|
|
174
|
+
const texts = content
|
|
175
|
+
.map((item) => {
|
|
176
|
+
if (typeof item === "string") return item;
|
|
177
|
+
if (item && typeof item === "object" && typeof item.text === "string") return item.text;
|
|
178
|
+
if (item && typeof item === "object" && (item.type === "image" || item.type === "document")) return factoryAttachmentLabel(item);
|
|
179
|
+
return "";
|
|
180
|
+
})
|
|
181
|
+
.filter(Boolean);
|
|
182
|
+
return { text: texts.join("\n"), detail: null };
|
|
183
|
+
}
|
|
184
|
+
if (content && typeof content === "object") {
|
|
185
|
+
if (content.diff || content.diffLines || content.success !== undefined) {
|
|
186
|
+
return { text: firstString(content.content, content.message), detail: content };
|
|
187
|
+
}
|
|
188
|
+
return { text: JSON.stringify(content), detail: null };
|
|
189
|
+
}
|
|
190
|
+
return { text: "", detail: null };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Factory file-op results store per-line diffs as
|
|
194
|
+
// {type: added|removed|unchanged|context, content, lineNumber: {old?, new?}}.
|
|
195
|
+
function factoryStructuredPatch(detail) {
|
|
196
|
+
const diffLines = Array.isArray(detail?.diffLines) ? detail.diffLines : [];
|
|
197
|
+
if (!diffLines.length) return [];
|
|
198
|
+
const lines = [];
|
|
199
|
+
let oldStart = 0;
|
|
200
|
+
let newStart = 0;
|
|
201
|
+
for (const item of diffLines) {
|
|
202
|
+
if (!item || typeof item !== "object") continue;
|
|
203
|
+
const type = String(item.type || "");
|
|
204
|
+
const prefix = type === "added" ? "+" : type === "removed" ? "-" : " ";
|
|
205
|
+
lines.push(`${prefix}${String(item.content ?? "")}`);
|
|
206
|
+
const oldLine = Number(item.lineNumber?.old);
|
|
207
|
+
const newLine = Number(item.lineNumber?.new);
|
|
208
|
+
if (!oldStart && Number.isFinite(oldLine) && oldLine > 0) oldStart = oldLine;
|
|
209
|
+
if (!newStart && Number.isFinite(newLine) && newLine > 0) newStart = newLine;
|
|
210
|
+
}
|
|
211
|
+
if (!lines.length) return [];
|
|
212
|
+
const hunk = { oldStart: oldStart || 1, newStart: newStart || 1, lines };
|
|
213
|
+
const file = firstString(detail?.file_path, detail?.filePath);
|
|
214
|
+
if (file) hunk.file = file;
|
|
215
|
+
return [hunk];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Sidecar <sessionId>.settings.json carries aggregate token usage and session
|
|
219
|
+
// configuration; there is no per-message usage in droid transcripts.
|
|
220
|
+
function factorySessionSummary(settings, header) {
|
|
221
|
+
if (!settings && !header) return null;
|
|
222
|
+
const tokenUsage = settings?.tokenUsage && typeof settings.tokenUsage === "object" ? settings.tokenUsage : null;
|
|
223
|
+
const usage = factoryUsage(tokenUsage);
|
|
224
|
+
const model = cleanFactoryModel(firstString(settings?.model));
|
|
225
|
+
return compactObject({
|
|
226
|
+
usage: usage || undefined,
|
|
227
|
+
modelUsage: model ? [{ model, source: "factory-session-settings" }] : undefined,
|
|
228
|
+
factoryDroid: compactObject({
|
|
229
|
+
model: firstString(settings?.model) || undefined,
|
|
230
|
+
reasoningEffort: firstString(settings?.reasoningEffort) || undefined,
|
|
231
|
+
autonomyMode: firstString(settings?.autonomyMode, settings?.autonomyLevel) || undefined,
|
|
232
|
+
specModeModel: firstString(settings?.specModeModel) || undefined,
|
|
233
|
+
providerLock: firstString(settings?.providerLock) || undefined,
|
|
234
|
+
apiProviderLock: firstString(settings?.apiProviderLock) || undefined,
|
|
235
|
+
factoryCredits: positiveNumber(tokenUsage?.factoryCredits),
|
|
236
|
+
assistantActiveTimeMs: positiveNumber(settings?.assistantActiveTimeMs),
|
|
237
|
+
archivedAt: toIso(settings?.archivedAt) || undefined,
|
|
238
|
+
sessionType: firstString(header?.decompSessionType) || undefined,
|
|
239
|
+
callingSessionId: firstString(header?.callingSessionId) || undefined,
|
|
240
|
+
parentSessionId: firstString(header?.parent) || undefined,
|
|
241
|
+
owner: firstString(header?.owner) || undefined,
|
|
242
|
+
subagentTokenUsageBySessionId:
|
|
243
|
+
settings?.childInclusiveTokenUsageBySessionId && typeof settings.childInclusiveTokenUsageBySessionId === "object" && Object.keys(settings.childInclusiveTokenUsageBySessionId).length
|
|
244
|
+
? settings.childInclusiveTokenUsageBySessionId
|
|
245
|
+
: undefined
|
|
246
|
+
})
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function factoryUsage(tokenUsage) {
|
|
251
|
+
if (!tokenUsage) return null;
|
|
252
|
+
const inputTokens = positiveNumber(tokenUsage.inputTokens);
|
|
253
|
+
const outputTokens = positiveNumber(tokenUsage.outputTokens);
|
|
254
|
+
const cacheReadTokens = positiveNumber(tokenUsage.cacheReadTokens ?? tokenUsage.cacheReadInputTokens);
|
|
255
|
+
const cacheCreationTokens = positiveNumber(tokenUsage.cacheCreationTokens ?? tokenUsage.cacheCreationInputTokens);
|
|
256
|
+
if (!inputTokens && !outputTokens && !cacheReadTokens && !cacheCreationTokens) return null;
|
|
257
|
+
return compactObject({
|
|
258
|
+
inputTokens,
|
|
259
|
+
outputTokens,
|
|
260
|
+
cacheReadTokens: cacheReadTokens || undefined,
|
|
261
|
+
cacheCreationInputTokens: cacheCreationTokens || undefined,
|
|
262
|
+
source: "factory-session-settings"
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// BYOK model ids look like "custom:Claude-Opus-4.5-Thinking-[Anthropic]-0".
|
|
267
|
+
function cleanFactoryModel(model) {
|
|
268
|
+
if (!model) return "";
|
|
269
|
+
return model
|
|
270
|
+
.replace(/^custom:/, "")
|
|
271
|
+
.replace(/-?\[[^\]]*\](-\d+)?$/, "")
|
|
272
|
+
.trim();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function factoryVisibility(value) {
|
|
276
|
+
const text = firstString(value);
|
|
277
|
+
return text && text !== "both" ? text : undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function normalizeFactoryRole(role) {
|
|
281
|
+
const text = String(role || "").toLowerCase();
|
|
282
|
+
if (text === "user" || text === "assistant" || text === "system" || text === "tool") return text;
|
|
283
|
+
return "assistant";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function normalizeFactoryContent(content) {
|
|
287
|
+
if (typeof content === "string") return content.trim() ? [{ type: "text", text: content }] : [];
|
|
288
|
+
if (Array.isArray(content)) return content;
|
|
289
|
+
if (content && typeof content === "object") return [content];
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function factoryAttachmentLabel(block) {
|
|
294
|
+
const mime = firstString(block?.source?.media_type, block?.source?.mediaType, block?.media_type, block?.mediaType);
|
|
295
|
+
return mime ? `[Attachment: ${mime}]` : "[Attachment]";
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function factoryToolCategory(name) {
|
|
299
|
+
const text = String(name || "");
|
|
300
|
+
if (/^task$/i.test(text) || /subagent/i.test(text)) return "task";
|
|
301
|
+
if (/execute|bash|shell|command/i.test(text)) return "shell";
|
|
302
|
+
if (/websearch|fetchurl|web|url|http/i.test(text)) return "web";
|
|
303
|
+
if (/edit|create|write|applypatch|patch|todowrite/i.test(text)) return "edit";
|
|
304
|
+
if (/^read$|^ls$|view/i.test(text)) return "read";
|
|
305
|
+
if (/grep|glob|find|search/i.test(text)) return "search";
|
|
306
|
+
if (/^mcp__/i.test(text)) return "mcp";
|
|
307
|
+
return "tool";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function summarizeToolArguments(value) {
|
|
311
|
+
if (value == null) return "";
|
|
312
|
+
if (typeof value === "string") return value.slice(0, 240);
|
|
313
|
+
if (typeof value !== "object") return String(value).slice(0, 240);
|
|
314
|
+
for (const key of ["command", "prompt", "query", "pattern", "file_path", "path", "url", "skill", "plan"]) {
|
|
315
|
+
if (value[key]) return String(value[key]).slice(0, 240);
|
|
316
|
+
}
|
|
317
|
+
return Object.entries(value)
|
|
318
|
+
.slice(0, 3)
|
|
319
|
+
.map(([key, item]) => `${key}: ${typeof item === "string" ? item : JSON.stringify(item)}`)
|
|
320
|
+
.join(", ")
|
|
321
|
+
.slice(0, 240);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function toolTarget(args) {
|
|
325
|
+
if (!args || typeof args !== "object") return "";
|
|
326
|
+
return firstString(args.file_path, args.filePath, args.path, args.directory_path, args.folder, args.url);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function toolDisplayName(value) {
|
|
330
|
+
const text = String(value || "tool").trim();
|
|
331
|
+
return text
|
|
332
|
+
.split(/_+|(?=[A-Z])/g)
|
|
333
|
+
.filter(Boolean)
|
|
334
|
+
.map((part) => part.replace(/(^|[-\s])([a-z])/g, (_, prefix, char) => `${prefix}${char.toUpperCase()}`))
|
|
335
|
+
.join(" ");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function firstLine(value) {
|
|
339
|
+
return String(value || "").split(/\r?\n/).find((line) => line.trim())?.trim() || "";
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function offsetTimestamp(base, index) {
|
|
343
|
+
const date = new Date(base);
|
|
344
|
+
if (Number.isNaN(date.valueOf())) return base;
|
|
345
|
+
return new Date(date.valueOf() + index * 1000).toISOString();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function toIso(value) {
|
|
349
|
+
if (!value) return "";
|
|
350
|
+
if (typeof value === "number") {
|
|
351
|
+
const ms = value > 1e12 ? value : value * 1000;
|
|
352
|
+
const date = new Date(ms);
|
|
353
|
+
return Number.isNaN(date.valueOf()) ? "" : date.toISOString();
|
|
354
|
+
}
|
|
355
|
+
const date = new Date(value);
|
|
356
|
+
return Number.isNaN(date.valueOf()) ? "" : date.toISOString();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function parseJson(text) {
|
|
360
|
+
if (typeof text !== "string" || !text.trim()) return null;
|
|
361
|
+
try {
|
|
362
|
+
return JSON.parse(text);
|
|
363
|
+
} catch {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function firstString(...values) {
|
|
369
|
+
for (const value of values) {
|
|
370
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
371
|
+
}
|
|
372
|
+
return "";
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function positiveNumber(value) {
|
|
376
|
+
const number = Number(value);
|
|
377
|
+
return Number.isFinite(number) && number > 0 ? number : undefined;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function compactObject(value) {
|
|
381
|
+
if (!value || typeof value !== "object") return value;
|
|
382
|
+
const result = {};
|
|
383
|
+
for (const [key, item] of Object.entries(value)) {
|
|
384
|
+
if (item === undefined || item === null || item === "") continue;
|
|
385
|
+
result[key] = item;
|
|
386
|
+
}
|
|
387
|
+
return Object.keys(result).length ? result : undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
module.exports = {
|
|
391
|
+
cleanFactoryModel,
|
|
392
|
+
factorySessionSummary,
|
|
393
|
+
factoryStructuredPatch,
|
|
394
|
+
parseFactorySessionFile,
|
|
395
|
+
parseFactorySessionText
|
|
396
|
+
};
|
package/src/importers/gemini.js
CHANGED
|
@@ -425,6 +425,7 @@ function normalizeGeminiToolCall(node, { event, part } = {}) {
|
|
|
425
425
|
(node.code ? { language: node.language, code: node.code } : undefined)
|
|
426
426
|
);
|
|
427
427
|
const summary = summarizeToolArguments(args);
|
|
428
|
+
const subagent = geminiSubagentToolMetadata(name, node, args, event);
|
|
428
429
|
return {
|
|
429
430
|
id: firstString(node.id, node.callId, node.call_id, event?.callId, event?.call_id, part?.id) || undefined,
|
|
430
431
|
name,
|
|
@@ -438,10 +439,41 @@ function normalizeGeminiToolCall(node, { event, part } = {}) {
|
|
|
438
439
|
inputPreview: summary,
|
|
439
440
|
target: toolTarget(args) || undefined,
|
|
440
441
|
arguments: args && typeof args === "object" && !Array.isArray(args) ? args : undefined,
|
|
442
|
+
agentId: subagent.agentId,
|
|
443
|
+
agentName: subagent.agentName,
|
|
444
|
+
prompt: subagent.prompt,
|
|
445
|
+
subagentProgress: subagent.subagentProgress,
|
|
446
|
+
resultPreview: subagent.resultPreview,
|
|
441
447
|
provider: PROVIDER
|
|
442
448
|
};
|
|
443
449
|
}
|
|
444
450
|
|
|
451
|
+
function geminiSubagentToolMetadata(name, node = {}, args = {}, event = {}) {
|
|
452
|
+
if (!geminiIsSubagentToolName(name)) return {};
|
|
453
|
+
const resultDisplay = node.resultDisplay || node.result_display || event.resultDisplay || event.result_display || {};
|
|
454
|
+
const progress = resultDisplay && typeof resultDisplay === "object"
|
|
455
|
+
? compactMetadata({
|
|
456
|
+
isSubagentProgress: resultDisplay.isSubagentProgress,
|
|
457
|
+
agentName: firstString(resultDisplay.agentName, resultDisplay.agent_name),
|
|
458
|
+
state: firstString(resultDisplay.state, resultDisplay.status),
|
|
459
|
+
terminateReason: firstString(resultDisplay.terminateReason, resultDisplay.terminate_reason),
|
|
460
|
+
result: previewString(firstString(resultDisplay.result, resultDisplay.output, resultDisplay.summary), 600),
|
|
461
|
+
recentActivity: Array.isArray(resultDisplay.recentActivity) ? resultDisplay.recentActivity.slice(0, 8) : undefined
|
|
462
|
+
})
|
|
463
|
+
: null;
|
|
464
|
+
return compactMetadata({
|
|
465
|
+
agentId: firstString(node.agentId, node.agent_id, event.agentId, event.agent_id, args?.agentId, args?.agent_id),
|
|
466
|
+
agentName: firstString(node.agentName, node.agent_name, resultDisplay.agentName, resultDisplay.agent_name, args?.agent_name, args?.agentName, args?.name),
|
|
467
|
+
prompt: firstString(args?.prompt, args?.task, args?.instructions),
|
|
468
|
+
subagentProgress: progress,
|
|
469
|
+
resultPreview: previewString(firstString(node.result, event.result, resultDisplay.result, resultDisplay.output, resultDisplay.summary), 600)
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function geminiIsSubagentToolName(name) {
|
|
474
|
+
return /^(invoke[_-]?agent|run[_-]?subagent|invoke[_-]?subagent)$/i.test(String(name || ""));
|
|
475
|
+
}
|
|
476
|
+
|
|
445
477
|
function normalizeGeminiToolResult(node, { event, part } = {}) {
|
|
446
478
|
if (!node || typeof node !== "object") return null;
|
|
447
479
|
const name = firstString(
|
|
@@ -999,6 +1031,7 @@ function normalizeGeminiRole(role) {
|
|
|
999
1031
|
|
|
1000
1032
|
function toolCategory(name, type = "") {
|
|
1001
1033
|
const text = `${name || ""} ${type || ""}`;
|
|
1034
|
+
if (/invoke[_-]?agent|subagent/i.test(text)) return "task";
|
|
1002
1035
|
if (/shell|bash|command|terminal|exec|npm|yarn|pnpm/i.test(text)) return "shell";
|
|
1003
1036
|
if (/web|fetch|browser|url|http/i.test(text)) return "web";
|
|
1004
1037
|
if (/edit|write|patch|replace|insert|update/i.test(text)) return "edit";
|
|
@@ -1127,6 +1160,12 @@ function asArray(value) {
|
|
|
1127
1160
|
return value == null ? [] : [value];
|
|
1128
1161
|
}
|
|
1129
1162
|
|
|
1163
|
+
function previewString(value, max = 600) {
|
|
1164
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
1165
|
+
if (!text) return "";
|
|
1166
|
+
return text.length > max ? `${text.slice(0, max - 3).trimEnd()}...` : text;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1130
1169
|
function compactMetadata(value) {
|
|
1131
1170
|
const output = {};
|
|
1132
1171
|
for (const [key, item] of Object.entries(value || {})) {
|