ultracontext 1.4.13 → 1.6.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.
- package/dist/cli/entry.mjs +9 -3
- package/dist/cli/entry.mjs.map +1 -1
- package/dist/cli/onboarding.mjs +268 -111
- package/dist/cli/onboarding.mjs.map +1 -1
- package/dist/cli/sdk-sync.mjs +199 -939
- package/dist/cli/sdk-sync.mjs.map +1 -1
- package/dist/cli/switch.mjs +168 -0
- package/dist/cli/switch.mjs.map +1 -0
- package/dist/{ctl-CXfNEPN8.mjs → ctl-DTQZxn3N.mjs} +2 -2
- package/dist/{ctl-CXfNEPN8.mjs.map → ctl-DTQZxn3N.mjs.map} +1 -1
- package/dist/hero-art-C03HmDXN.mjs +46 -0
- package/dist/hero-art-C03HmDXN.mjs.map +1 -0
- package/dist/index.d.mts +21 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +25 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{launcher-BMMjzr5k.mjs → launcher-ZylswrpR.mjs} +3 -3
- package/dist/{launcher-BMMjzr5k.mjs.map → launcher-ZylswrpR.mjs.map} +1 -1
- package/dist/{lock-5aJnda81.mjs → lock-BhZX2aF3.mjs} +2 -2
- package/dist/{lock-5aJnda81.mjs.map → lock-BhZX2aF3.mjs.map} +1 -1
- package/dist/onboarding-preferences-Alhblobi.mjs +76 -0
- package/dist/onboarding-preferences-Alhblobi.mjs.map +1 -0
- package/dist/src-Bovo1ukU.mjs +1200 -0
- package/dist/src-Bovo1ukU.mjs.map +1 -0
- package/dist/{tui-DZ1SDOH2.mjs → tui-DLEjew3K.mjs} +334 -115
- package/dist/tui-DLEjew3K.mjs.map +1 -0
- package/dist/utils-BTfShW0g.mjs +36 -0
- package/dist/utils-BTfShW0g.mjs.map +1 -0
- package/dist/{utils-CmuIYHtm.mjs → utils-D9CKnbke.mjs} +26 -34
- package/dist/utils-D9CKnbke.mjs.map +1 -0
- package/lib/register-skills.mjs +96 -0
- package/package.json +8 -3
- package/plugin/.claude-plugin/plugin.json +6 -0
- package/plugin/README.md +112 -0
- package/plugin/marketplace.json +17 -0
- package/plugin/skills/switch/SKILL.md +27 -0
- package/postinstall.mjs +35 -2
- package/dist/Spinner-CwBjkXHv.mjs +0 -153
- package/dist/Spinner-CwBjkXHv.mjs.map +0 -1
- package/dist/tui-DZ1SDOH2.mjs.map +0 -1
- package/dist/utils-CmuIYHtm.mjs.map +0 -1
package/dist/cli/sdk-sync.mjs
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as parseCursorLine, d as parseClaudeCodeLine, l as parseOpenClawLine, o as parseGstackLine, s as parseGeminiFile, u as parseCodexLine } from "../src-Bovo1ukU.mjs";
|
|
2
2
|
import { n as normalizeBootstrapMode, t as createBootstrapStateKey } from "../protocol-BI9ficcl.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { a as isPrimaryAgentSourceEnabled, c as normalizeProjectPaths, o as matchesConfiguredProjectPath, s as normalizeCaptureAgents } from "../onboarding-preferences-Alhblobi.mjs";
|
|
4
|
+
import { i as expandHome, m as truncateString } from "../utils-D9CKnbke.mjs";
|
|
5
|
+
import { i as toInt, n as extractProjectPathFromFile, r as sha256, t as boolFromEnv } from "../utils-BTfShW0g.mjs";
|
|
6
|
+
import { n as resolveLockPath, t as acquireFileLock } from "../lock-BhZX2aF3.mjs";
|
|
4
7
|
import process$1 from "node:process";
|
|
5
8
|
import path from "node:path";
|
|
6
9
|
import fs from "node:fs";
|
|
7
10
|
import os from "node:os";
|
|
8
11
|
import { spawnSync } from "node:child_process";
|
|
9
12
|
import { DatabaseSync } from "node:sqlite";
|
|
10
|
-
import { randomUUID } from "node:crypto";
|
|
11
13
|
import fs$1 from "node:fs/promises";
|
|
14
|
+
import readline from "node:readline";
|
|
12
15
|
import fg from "fast-glob";
|
|
13
16
|
import { UltraContext } from "ultracontext";
|
|
14
17
|
|
|
@@ -179,920 +182,6 @@ function redact(value, currentKey = "") {
|
|
|
179
182
|
return REDACTED;
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
//#endregion
|
|
183
|
-
//#region ../../packages/parsers/src/agents/claude.mjs
|
|
184
|
-
function formatToolUse(item) {
|
|
185
|
-
const name = (item.name ?? "unknown").toLowerCase();
|
|
186
|
-
const input = item.input ?? {};
|
|
187
|
-
const filePath = input.file_path ?? input.path ?? "";
|
|
188
|
-
if (name === "write") return `[Write] ${filePath}\n${preserveText(input.content ?? input.file_text ?? "")}`;
|
|
189
|
-
if (name === "edit") {
|
|
190
|
-
const parts = [`[Edit] ${filePath}`];
|
|
191
|
-
if (input.old_string) parts.push(`- ${preserveText(input.old_string)}`);
|
|
192
|
-
if (input.new_string) parts.push(`+ ${preserveText(input.new_string)}`);
|
|
193
|
-
return parts.join("\n");
|
|
194
|
-
}
|
|
195
|
-
if (name === "read") return `[Read] ${filePath}`;
|
|
196
|
-
if (name === "bash") return `[Bash] ${preserveText(input.command ?? "")}`;
|
|
197
|
-
if (name === "grep" || name === "glob") {
|
|
198
|
-
const loc = filePath ? ` in ${filePath}` : "";
|
|
199
|
-
return `[${item.name}] ${input.pattern ?? ""}${loc}`;
|
|
200
|
-
}
|
|
201
|
-
const compact = JSON.stringify(input);
|
|
202
|
-
return `[${item.name ?? name}] ${compact.length > 500 ? compact.slice(0, 500) + "..." : compact}`;
|
|
203
|
-
}
|
|
204
|
-
function formatToolResult(item) {
|
|
205
|
-
const content = item.content ?? "";
|
|
206
|
-
if (typeof content === "string") {
|
|
207
|
-
const text = preserveText(content);
|
|
208
|
-
return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
|
|
209
|
-
}
|
|
210
|
-
if (Array.isArray(content)) {
|
|
211
|
-
const text = content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => preserveText(c.text)).filter(Boolean).join("\n");
|
|
212
|
-
return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
|
|
213
|
-
}
|
|
214
|
-
return "[result] ok";
|
|
215
|
-
}
|
|
216
|
-
function extractClaudeTextContent(content) {
|
|
217
|
-
if (!content) return "";
|
|
218
|
-
if (typeof content === "string") return preserveText(content);
|
|
219
|
-
if (Array.isArray(content)) {
|
|
220
|
-
const parts = [];
|
|
221
|
-
for (const item of content) {
|
|
222
|
-
if (!item || typeof item !== "object") continue;
|
|
223
|
-
if (item.type === "text" && typeof item.text === "string") {
|
|
224
|
-
const chunk = preserveText(item.text);
|
|
225
|
-
if (chunk) parts.push(chunk);
|
|
226
|
-
}
|
|
227
|
-
if (item.type === "thinking" && typeof item.thinking === "string") {
|
|
228
|
-
const chunk = preserveText(item.thinking);
|
|
229
|
-
if (chunk) parts.push(`[thinking] ${chunk}`);
|
|
230
|
-
}
|
|
231
|
-
if (item.type === "tool_use") parts.push(formatToolUse(item));
|
|
232
|
-
if (item.type === "tool_result") parts.push(formatToolResult(item));
|
|
233
|
-
}
|
|
234
|
-
return parts.join("\n\n");
|
|
235
|
-
}
|
|
236
|
-
if (typeof content === "object") {
|
|
237
|
-
if (typeof content.text === "string") return preserveText(content.text);
|
|
238
|
-
if (typeof content.content === "string") return preserveText(content.content);
|
|
239
|
-
}
|
|
240
|
-
return "";
|
|
241
|
-
}
|
|
242
|
-
function parseClaudeCodeLine({ line, filePath }) {
|
|
243
|
-
const parsed = safeJsonParse(line);
|
|
244
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
245
|
-
const type = String(parsed.type ?? "").toLowerCase();
|
|
246
|
-
const sessionId = parsed.sessionId ?? parsed.session_id ?? parsed.payload?.sessionId ?? parsed.payload?.session_id ?? extractSessionIdFromPath(filePath);
|
|
247
|
-
const timestamp = parsed.timestamp ?? parsed.ts ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
248
|
-
if (type === "summary") {
|
|
249
|
-
const summary = normalizeWhitespace(parsed.summary);
|
|
250
|
-
if (!summary) return null;
|
|
251
|
-
return {
|
|
252
|
-
sessionId,
|
|
253
|
-
eventType: "claude.summary",
|
|
254
|
-
kind: "system",
|
|
255
|
-
timestamp,
|
|
256
|
-
message: toMessage(summary),
|
|
257
|
-
raw: parsed
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
if (type === "file-history-snapshot") {
|
|
261
|
-
const trackedFiles = Object.keys(parsed.snapshot?.trackedFileBackups ?? {}).length;
|
|
262
|
-
return {
|
|
263
|
-
sessionId,
|
|
264
|
-
eventType: "claude.file_snapshot",
|
|
265
|
-
kind: "system",
|
|
266
|
-
timestamp,
|
|
267
|
-
message: toMessage(`${parsed.isSnapshotUpdate ? "File snapshot update" : "File snapshot"}: ${trackedFiles} tracked files`),
|
|
268
|
-
raw: parsed
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
if (type === "system") {
|
|
272
|
-
const subtype = parsed.subtype ?? "unknown";
|
|
273
|
-
let message;
|
|
274
|
-
if (subtype === "local_command") message = parsed.content ? preserveText(parsed.content) : "Local command executed";
|
|
275
|
-
else if (subtype === "stop_hook_summary") message = `Hook summary: ${parsed.hookCount ?? 0} hooks`;
|
|
276
|
-
else if (subtype === "turn_duration") message = `Turn completed in ${parsed.durationMs ?? 0}ms (${parsed.messageCount ?? 0} messages)`;
|
|
277
|
-
else message = `System event: ${subtype}`;
|
|
278
|
-
if (!message) return null;
|
|
279
|
-
return {
|
|
280
|
-
sessionId,
|
|
281
|
-
eventType: `claude.system.${subtype}`,
|
|
282
|
-
kind: "system",
|
|
283
|
-
timestamp,
|
|
284
|
-
message: toMessage(message),
|
|
285
|
-
raw: parsed
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
if (type === "attachment") {
|
|
289
|
-
const attachmentType = parsed.attachment?.type ?? "unknown";
|
|
290
|
-
const parts = [];
|
|
291
|
-
const added = parsed.attachment?.addedNames;
|
|
292
|
-
const removed = parsed.attachment?.removedNames;
|
|
293
|
-
if (Array.isArray(added) && added.length) parts.push(`added: ${added.join(", ")}`);
|
|
294
|
-
if (Array.isArray(removed) && removed.length) parts.push(`removed: ${removed.join(", ")}`);
|
|
295
|
-
const detail = parts.length ? ` (${parts.join("; ")})` : "";
|
|
296
|
-
return {
|
|
297
|
-
sessionId,
|
|
298
|
-
eventType: `claude.attachment.${attachmentType}`,
|
|
299
|
-
kind: "system",
|
|
300
|
-
timestamp,
|
|
301
|
-
message: toMessage(`Attachment: ${attachmentType}${detail}`),
|
|
302
|
-
raw: parsed
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
if (type === "progress") {
|
|
306
|
-
const data = parsed.data ?? {};
|
|
307
|
-
const subtype = data.type ?? "unknown";
|
|
308
|
-
const label = data.hookName ?? subtype;
|
|
309
|
-
return {
|
|
310
|
-
sessionId,
|
|
311
|
-
eventType: `claude.progress.${subtype}`,
|
|
312
|
-
kind: "system",
|
|
313
|
-
timestamp,
|
|
314
|
-
message: toMessage(`Progress: ${subtype} (${label})`),
|
|
315
|
-
raw: parsed
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
if (type === "queue-operation") return {
|
|
319
|
-
sessionId,
|
|
320
|
-
eventType: "claude.queue_operation",
|
|
321
|
-
kind: "system",
|
|
322
|
-
timestamp,
|
|
323
|
-
message: toMessage(`Queue ${parsed.operation ?? "unknown"}${parsed.content ? `: ${parsed.content}` : ""}`),
|
|
324
|
-
raw: parsed
|
|
325
|
-
};
|
|
326
|
-
if (type === "custom-title") return {
|
|
327
|
-
sessionId,
|
|
328
|
-
eventType: "claude.custom_title",
|
|
329
|
-
kind: "system",
|
|
330
|
-
timestamp,
|
|
331
|
-
message: toMessage(`Custom title: ${parsed.customTitle ?? "unknown"}`),
|
|
332
|
-
raw: parsed
|
|
333
|
-
};
|
|
334
|
-
if (type === "agent-name") return {
|
|
335
|
-
sessionId,
|
|
336
|
-
eventType: "claude.agent_name",
|
|
337
|
-
kind: "system",
|
|
338
|
-
timestamp,
|
|
339
|
-
message: toMessage(`Agent name: ${parsed.agentName ?? "unknown"}`),
|
|
340
|
-
raw: parsed
|
|
341
|
-
};
|
|
342
|
-
if (type === "pr-link") return {
|
|
343
|
-
sessionId,
|
|
344
|
-
eventType: "claude.pr_link",
|
|
345
|
-
kind: "system",
|
|
346
|
-
timestamp,
|
|
347
|
-
message: toMessage(`PR #${parsed.prNumber ?? 0}: ${parsed.prUrl ?? ""}`),
|
|
348
|
-
raw: parsed
|
|
349
|
-
};
|
|
350
|
-
if (type === "permission-mode") return {
|
|
351
|
-
sessionId,
|
|
352
|
-
eventType: "claude.permission_mode",
|
|
353
|
-
kind: "system",
|
|
354
|
-
timestamp,
|
|
355
|
-
message: toMessage(`Permission mode: ${parsed.permissionMode ?? "unknown"}`),
|
|
356
|
-
raw: parsed
|
|
357
|
-
};
|
|
358
|
-
if (type === "last-prompt") {
|
|
359
|
-
const prompt = parsed.lastPrompt ?? "";
|
|
360
|
-
if (!prompt) return null;
|
|
361
|
-
return {
|
|
362
|
-
sessionId,
|
|
363
|
-
eventType: "claude.last_prompt",
|
|
364
|
-
kind: "system",
|
|
365
|
-
timestamp,
|
|
366
|
-
message: toMessage(prompt),
|
|
367
|
-
raw: parsed
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
if (type === "ai-title") return {
|
|
371
|
-
sessionId,
|
|
372
|
-
eventType: "claude.ai_title",
|
|
373
|
-
kind: "system",
|
|
374
|
-
timestamp,
|
|
375
|
-
message: toMessage(`AI title: ${parsed.aiTitle ?? "unknown"}`),
|
|
376
|
-
raw: parsed
|
|
377
|
-
};
|
|
378
|
-
if (type === "task-summary") return {
|
|
379
|
-
sessionId,
|
|
380
|
-
eventType: "claude.task_summary",
|
|
381
|
-
kind: "system",
|
|
382
|
-
timestamp,
|
|
383
|
-
message: toMessage(parsed.summary ?? "[task summary]"),
|
|
384
|
-
raw: parsed
|
|
385
|
-
};
|
|
386
|
-
if (type === "tag") return {
|
|
387
|
-
sessionId,
|
|
388
|
-
eventType: "claude.tag",
|
|
389
|
-
kind: "system",
|
|
390
|
-
timestamp,
|
|
391
|
-
message: toMessage(`Tag: ${parsed.tag ?? ""}`),
|
|
392
|
-
raw: parsed
|
|
393
|
-
};
|
|
394
|
-
if (type === "agent-color") return {
|
|
395
|
-
sessionId,
|
|
396
|
-
eventType: "claude.agent_color",
|
|
397
|
-
kind: "system",
|
|
398
|
-
timestamp,
|
|
399
|
-
message: toMessage(`Agent color: ${parsed.agentColor ?? "unknown"}`),
|
|
400
|
-
raw: parsed
|
|
401
|
-
};
|
|
402
|
-
if (type === "agent-setting") return {
|
|
403
|
-
sessionId,
|
|
404
|
-
eventType: "claude.agent_setting",
|
|
405
|
-
kind: "system",
|
|
406
|
-
timestamp,
|
|
407
|
-
message: toMessage(`Agent setting: ${parsed.agentSetting ?? "unknown"}`),
|
|
408
|
-
raw: parsed
|
|
409
|
-
};
|
|
410
|
-
if (type === "mode") return {
|
|
411
|
-
sessionId,
|
|
412
|
-
eventType: "claude.mode",
|
|
413
|
-
kind: "system",
|
|
414
|
-
timestamp,
|
|
415
|
-
message: toMessage(`Mode: ${parsed.mode ?? "unknown"}`),
|
|
416
|
-
raw: parsed
|
|
417
|
-
};
|
|
418
|
-
if (type === "worktree-state") {
|
|
419
|
-
const ws = parsed.worktreeSession;
|
|
420
|
-
return {
|
|
421
|
-
sessionId,
|
|
422
|
-
eventType: "claude.worktree_state",
|
|
423
|
-
kind: "system",
|
|
424
|
-
timestamp,
|
|
425
|
-
message: toMessage(`Worktree: ${ws ? `entered ${ws.worktreePath ?? ""}` : "exited"}`),
|
|
426
|
-
raw: parsed
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
if (type === "attribution-snapshot") {
|
|
430
|
-
const fileCount = Object.keys(parsed.fileStates ?? {}).length;
|
|
431
|
-
return {
|
|
432
|
-
sessionId,
|
|
433
|
-
eventType: "claude.attribution_snapshot",
|
|
434
|
-
kind: "system",
|
|
435
|
-
timestamp,
|
|
436
|
-
message: toMessage(`Attribution snapshot: ${fileCount} files`),
|
|
437
|
-
raw: parsed
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
if (type === "content-replacement") return {
|
|
441
|
-
sessionId,
|
|
442
|
-
eventType: "claude.content_replacement",
|
|
443
|
-
kind: "system",
|
|
444
|
-
timestamp,
|
|
445
|
-
message: toMessage(`Content replacement: ${parsed.replacements?.length ?? 0} blocks`),
|
|
446
|
-
raw: parsed
|
|
447
|
-
};
|
|
448
|
-
if (type === "speculation-accept") return {
|
|
449
|
-
sessionId,
|
|
450
|
-
eventType: "claude.speculation_accept",
|
|
451
|
-
kind: "system",
|
|
452
|
-
timestamp,
|
|
453
|
-
message: toMessage(`Speculation accepted: saved ${parsed.timeSavedMs ?? 0}ms`),
|
|
454
|
-
raw: parsed
|
|
455
|
-
};
|
|
456
|
-
if (type === "marble-origami-commit") return {
|
|
457
|
-
sessionId,
|
|
458
|
-
eventType: "claude.context_collapse_commit",
|
|
459
|
-
kind: "system",
|
|
460
|
-
timestamp,
|
|
461
|
-
message: toMessage(parsed.summary ?? `Context collapse: ${parsed.collapseId ?? ""}`),
|
|
462
|
-
raw: parsed
|
|
463
|
-
};
|
|
464
|
-
if (type === "marble-origami-snapshot") return {
|
|
465
|
-
sessionId,
|
|
466
|
-
eventType: "claude.context_collapse_snapshot",
|
|
467
|
-
kind: "system",
|
|
468
|
-
timestamp,
|
|
469
|
-
message: toMessage(`Context collapse snapshot: ${parsed.staged?.length ?? 0} staged`),
|
|
470
|
-
raw: parsed
|
|
471
|
-
};
|
|
472
|
-
if (type !== "user" && type !== "assistant") return null;
|
|
473
|
-
const rawContent = parsed.message?.content ?? parsed.message ?? parsed.content ?? parsed.payload?.content ?? "";
|
|
474
|
-
let message = extractClaudeTextContent(rawContent);
|
|
475
|
-
if (!message) {
|
|
476
|
-
if (!(Array.isArray(rawContent) && rawContent.some((c) => c?.type === "thinking"))) return null;
|
|
477
|
-
message = "[thinking]";
|
|
478
|
-
}
|
|
479
|
-
const roleHint = parsed.message?.role ?? type;
|
|
480
|
-
return {
|
|
481
|
-
sessionId,
|
|
482
|
-
eventType: `claude.${type}`,
|
|
483
|
-
kind: normalizeRole(roleHint, type === "user" ? "user" : "assistant"),
|
|
484
|
-
timestamp,
|
|
485
|
-
message: toMessage(message),
|
|
486
|
-
raw: parsed
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
//#endregion
|
|
491
|
-
//#region ../../packages/parsers/src/agents/codex.mjs
|
|
492
|
-
function parseCodexLine({ line, filePath }) {
|
|
493
|
-
const parsed = safeJsonParse(line);
|
|
494
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
495
|
-
const payload = parsed.payload ?? {};
|
|
496
|
-
const sessionId = payload.session_id ?? payload.id ?? parsed.session_id ?? extractSessionIdFromPath(filePath);
|
|
497
|
-
if (parsed.type === "event_msg") {
|
|
498
|
-
const eventType = payload.type ?? "unknown";
|
|
499
|
-
if (![
|
|
500
|
-
"user_message",
|
|
501
|
-
"agent_message",
|
|
502
|
-
"task_started",
|
|
503
|
-
"task_complete",
|
|
504
|
-
"token_count",
|
|
505
|
-
"agent_reasoning",
|
|
506
|
-
"turn_aborted",
|
|
507
|
-
"context_compacted"
|
|
508
|
-
].includes(eventType)) return null;
|
|
509
|
-
const kind = eventType === "user_message" ? "user" : eventType === "agent_message" ? "assistant" : "system";
|
|
510
|
-
const message = eventType === "token_count" ? "Token count update" : eventType === "agent_reasoning" ? payload.text : eventType === "turn_aborted" ? payload.reason ? `Turn aborted: ${payload.reason}` : "Turn aborted" : eventType === "context_compacted" ? "Context compacted" : payload.message ?? payload.last_agent_message ?? `${eventType}${payload.turn_id ? ` (${payload.turn_id})` : ""}`;
|
|
511
|
-
return {
|
|
512
|
-
sessionId,
|
|
513
|
-
eventType: `event_msg.${eventType}`,
|
|
514
|
-
kind,
|
|
515
|
-
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
516
|
-
message: toMessage(message),
|
|
517
|
-
raw: parsed
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
if (parsed.type === "session_meta") return {
|
|
521
|
-
sessionId,
|
|
522
|
-
eventType: "session_meta",
|
|
523
|
-
kind: "system",
|
|
524
|
-
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
525
|
-
message: `Session started in ${payload.cwd ?? "unknown cwd"}`,
|
|
526
|
-
raw: parsed
|
|
527
|
-
};
|
|
528
|
-
if (parsed.type === "response_item") {
|
|
529
|
-
const subtype = payload.type;
|
|
530
|
-
const ts = parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
531
|
-
if (subtype === "message") {
|
|
532
|
-
const text = (payload.content ?? []).map((c) => c.text ?? "").filter(Boolean).join("\n");
|
|
533
|
-
return {
|
|
534
|
-
sessionId,
|
|
535
|
-
eventType: "response_item.message",
|
|
536
|
-
kind: {
|
|
537
|
-
developer: "system",
|
|
538
|
-
assistant: "assistant",
|
|
539
|
-
user: "user"
|
|
540
|
-
}[payload.role] ?? normalizeRole(payload.role),
|
|
541
|
-
timestamp: ts,
|
|
542
|
-
message: toMessage(text || `[${payload.role ?? "unknown"} message]`),
|
|
543
|
-
raw: parsed
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
if (subtype === "reasoning") return {
|
|
547
|
-
sessionId,
|
|
548
|
-
eventType: "response_item.reasoning",
|
|
549
|
-
kind: "system",
|
|
550
|
-
timestamp: ts,
|
|
551
|
-
message: toMessage((payload.summary ?? []).map((s) => s.text ?? "").filter(Boolean).join("\n") || "[reasoning]"),
|
|
552
|
-
raw: parsed
|
|
553
|
-
};
|
|
554
|
-
if (subtype === "function_call") return {
|
|
555
|
-
sessionId,
|
|
556
|
-
eventType: "response_item.function_call",
|
|
557
|
-
kind: "system",
|
|
558
|
-
timestamp: ts,
|
|
559
|
-
message: toMessage(`[${payload.name ?? "unknown"}] ${payload.arguments ?? ""}`),
|
|
560
|
-
raw: parsed
|
|
561
|
-
};
|
|
562
|
-
if (subtype === "function_call_output") return {
|
|
563
|
-
sessionId,
|
|
564
|
-
eventType: "response_item.function_call_output",
|
|
565
|
-
kind: "system",
|
|
566
|
-
timestamp: ts,
|
|
567
|
-
message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
|
|
568
|
-
raw: parsed
|
|
569
|
-
};
|
|
570
|
-
if (subtype === "web_search_call") {
|
|
571
|
-
const query = payload.action?.query;
|
|
572
|
-
return {
|
|
573
|
-
sessionId,
|
|
574
|
-
eventType: "response_item.web_search_call",
|
|
575
|
-
kind: "system",
|
|
576
|
-
timestamp: ts,
|
|
577
|
-
message: query ? `[web_search] ${query}` : "[web_search]",
|
|
578
|
-
raw: parsed
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
if (subtype === "custom_tool_call") return {
|
|
582
|
-
sessionId,
|
|
583
|
-
eventType: "response_item.custom_tool_call",
|
|
584
|
-
kind: "system",
|
|
585
|
-
timestamp: ts,
|
|
586
|
-
message: toMessage(`[${payload.name ?? "unknown"}] ${payload.input ?? ""}`),
|
|
587
|
-
raw: parsed
|
|
588
|
-
};
|
|
589
|
-
if (subtype === "custom_tool_call_output") return {
|
|
590
|
-
sessionId,
|
|
591
|
-
eventType: "response_item.custom_tool_call_output",
|
|
592
|
-
kind: "system",
|
|
593
|
-
timestamp: ts,
|
|
594
|
-
message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
|
|
595
|
-
raw: parsed
|
|
596
|
-
};
|
|
597
|
-
return null;
|
|
598
|
-
}
|
|
599
|
-
if (parsed.type === "compacted") return {
|
|
600
|
-
sessionId,
|
|
601
|
-
eventType: "compacted",
|
|
602
|
-
kind: "system",
|
|
603
|
-
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
604
|
-
message: "Session compacted",
|
|
605
|
-
raw: parsed
|
|
606
|
-
};
|
|
607
|
-
if (parsed.type === "turn_context") return {
|
|
608
|
-
sessionId,
|
|
609
|
-
eventType: "turn_context",
|
|
610
|
-
kind: "system",
|
|
611
|
-
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
612
|
-
message: `Turn context: model=${payload.model}, policy=${payload.approval_policy}`,
|
|
613
|
-
raw: parsed
|
|
614
|
-
};
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
//#endregion
|
|
619
|
-
//#region ../../packages/parsers/src/agents/openclaw.mjs
|
|
620
|
-
function extractOpenClawTextContent(content) {
|
|
621
|
-
if (!content) return "";
|
|
622
|
-
if (typeof content === "string") return normalizeWhitespace(content);
|
|
623
|
-
if (Array.isArray(content)) {
|
|
624
|
-
const textParts = [];
|
|
625
|
-
for (const item of content) {
|
|
626
|
-
if (!item || typeof item !== "object") continue;
|
|
627
|
-
if (item.type === "text" && typeof item.text === "string") {
|
|
628
|
-
const chunk = normalizeWhitespace(item.text);
|
|
629
|
-
if (chunk) textParts.push(chunk);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
return textParts.join("\n");
|
|
633
|
-
}
|
|
634
|
-
if (typeof content === "object" && typeof content.text === "string") return normalizeWhitespace(content.text);
|
|
635
|
-
return "";
|
|
636
|
-
}
|
|
637
|
-
function extractOpenClawToolCalls(content) {
|
|
638
|
-
if (!Array.isArray(content)) return [];
|
|
639
|
-
const names = [];
|
|
640
|
-
for (const item of content) {
|
|
641
|
-
if (!item || typeof item !== "object" || item.type !== "toolCall") continue;
|
|
642
|
-
const name = normalizeWhitespace(item.name);
|
|
643
|
-
if (name) names.push(name);
|
|
644
|
-
}
|
|
645
|
-
return names;
|
|
646
|
-
}
|
|
647
|
-
function buildOpenClawRaw(parsed) {
|
|
648
|
-
const raw = {
|
|
649
|
-
type: parsed.type,
|
|
650
|
-
id: parsed.id,
|
|
651
|
-
parentId: parsed.parentId,
|
|
652
|
-
timestamp: parsed.timestamp
|
|
653
|
-
};
|
|
654
|
-
if (parsed.type === "session") {
|
|
655
|
-
raw.session = {
|
|
656
|
-
id: parsed.id,
|
|
657
|
-
version: parsed.version,
|
|
658
|
-
cwd: parsed.cwd,
|
|
659
|
-
parentSession: parsed.parentSession
|
|
660
|
-
};
|
|
661
|
-
return raw;
|
|
662
|
-
}
|
|
663
|
-
if (parsed.type === "custom") {
|
|
664
|
-
raw.customType = parsed.customType;
|
|
665
|
-
if (parsed.customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") raw.data = {
|
|
666
|
-
provider: parsed.data.provider,
|
|
667
|
-
modelApi: parsed.data.modelApi,
|
|
668
|
-
modelId: parsed.data.modelId,
|
|
669
|
-
timestamp: parsed.data.timestamp
|
|
670
|
-
};
|
|
671
|
-
return raw;
|
|
672
|
-
}
|
|
673
|
-
if (parsed.message && typeof parsed.message === "object") {
|
|
674
|
-
const contentTypes = Array.isArray(parsed.message.content) ? parsed.message.content.filter((item) => item && typeof item === "object").map((item) => String(item.type ?? "unknown")).slice(0, 12) : [];
|
|
675
|
-
raw.message = {
|
|
676
|
-
role: parsed.message.role,
|
|
677
|
-
stopReason: parsed.message.stopReason,
|
|
678
|
-
toolName: parsed.message.toolName,
|
|
679
|
-
toolCallId: parsed.message.toolCallId,
|
|
680
|
-
isError: parsed.message.isError,
|
|
681
|
-
contentTypes
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
if (parsed.type === "compaction") raw.compaction = {
|
|
685
|
-
firstKeptEntryId: parsed.firstKeptEntryId,
|
|
686
|
-
tokensBefore: parsed.tokensBefore
|
|
687
|
-
};
|
|
688
|
-
else if (parsed.type === "branch_summary") raw.branchSummary = {
|
|
689
|
-
firstKeptEntryId: parsed.firstKeptEntryId,
|
|
690
|
-
summary: typeof parsed.summary === "string" ? truncateString(parsed.summary, 350) : ""
|
|
691
|
-
};
|
|
692
|
-
return raw;
|
|
693
|
-
}
|
|
694
|
-
function parseOpenClawLine({ line, filePath }) {
|
|
695
|
-
const parsed = safeJsonParse(line);
|
|
696
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
697
|
-
const type = String(parsed.type ?? "").toLowerCase();
|
|
698
|
-
const sessionId = parsed.session_id ?? parsed.sessionId ?? parsed.message?.session_id ?? parsed.message?.sessionId ?? extractSessionIdFromPath(filePath);
|
|
699
|
-
const timestamp = parsed.timestamp ?? parsed.message?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
700
|
-
if (type === "session") return {
|
|
701
|
-
sessionId,
|
|
702
|
-
eventType: "openclaw.session",
|
|
703
|
-
kind: "system",
|
|
704
|
-
timestamp,
|
|
705
|
-
message: toMessage(`Session started in ${parsed.cwd ?? "unknown cwd"}`),
|
|
706
|
-
raw: buildOpenClawRaw(parsed)
|
|
707
|
-
};
|
|
708
|
-
if (type === "custom") {
|
|
709
|
-
const customType = normalizeWhitespace(parsed.customType || "custom");
|
|
710
|
-
if (customType === "openclaw.cache-ttl") return null;
|
|
711
|
-
let message = `Custom event: ${customType}`;
|
|
712
|
-
if (customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") {
|
|
713
|
-
const provider = normalizeWhitespace(parsed.data.provider || "");
|
|
714
|
-
const modelId = normalizeWhitespace(parsed.data.modelId || "");
|
|
715
|
-
const modelApi = normalizeWhitespace(parsed.data.modelApi || "");
|
|
716
|
-
const details = [provider, modelId].filter(Boolean).join("/");
|
|
717
|
-
message = `Model snapshot${details ? `: ${details}` : ""}${modelApi ? ` (${modelApi})` : ""}`;
|
|
718
|
-
}
|
|
719
|
-
return {
|
|
720
|
-
sessionId,
|
|
721
|
-
eventType: `openclaw.custom.${customType || "custom"}`,
|
|
722
|
-
kind: "system",
|
|
723
|
-
timestamp,
|
|
724
|
-
message: toMessage(message),
|
|
725
|
-
raw: buildOpenClawRaw(parsed)
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
if (type === "compaction") return {
|
|
729
|
-
sessionId,
|
|
730
|
-
eventType: "openclaw.compaction",
|
|
731
|
-
kind: "system",
|
|
732
|
-
timestamp,
|
|
733
|
-
message: toMessage("Session compaction summary updated"),
|
|
734
|
-
raw: buildOpenClawRaw(parsed)
|
|
735
|
-
};
|
|
736
|
-
if (type === "branch_summary") return {
|
|
737
|
-
sessionId,
|
|
738
|
-
eventType: "openclaw.branch_summary",
|
|
739
|
-
kind: "system",
|
|
740
|
-
timestamp,
|
|
741
|
-
message: toMessage(normalizeWhitespace(parsed.summary || "") || "Branch summary updated"),
|
|
742
|
-
raw: buildOpenClawRaw(parsed)
|
|
743
|
-
};
|
|
744
|
-
if (type !== "message" && type !== "custom_message") return null;
|
|
745
|
-
const eventMessage = parsed.message ?? {};
|
|
746
|
-
const role = String(eventMessage.role ?? "").toLowerCase();
|
|
747
|
-
if (role === "user" || role === "assistant") {
|
|
748
|
-
const text = extractOpenClawTextContent(eventMessage.content);
|
|
749
|
-
if (text) return {
|
|
750
|
-
sessionId,
|
|
751
|
-
eventType: `openclaw.${role}`,
|
|
752
|
-
kind: role === "user" ? "user" : "assistant",
|
|
753
|
-
timestamp,
|
|
754
|
-
message: toMessage(text),
|
|
755
|
-
raw: buildOpenClawRaw(parsed)
|
|
756
|
-
};
|
|
757
|
-
if (role === "assistant") {
|
|
758
|
-
const toolCalls = extractOpenClawToolCalls(eventMessage.content);
|
|
759
|
-
if (toolCalls.length > 0) return {
|
|
760
|
-
sessionId,
|
|
761
|
-
eventType: "openclaw.assistant.tool_use",
|
|
762
|
-
kind: "system",
|
|
763
|
-
timestamp,
|
|
764
|
-
message: toMessage(`Assistant requested tools: ${toolCalls.slice(0, 5).join(", ")}${toolCalls.length > 5 ? ` (+${toolCalls.length - 5})` : ""}`),
|
|
765
|
-
raw: buildOpenClawRaw(parsed)
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
if (role === "toolresult") {
|
|
771
|
-
const toolName = normalizeWhitespace(eventMessage.toolName || "");
|
|
772
|
-
const isError = Boolean(eventMessage.isError);
|
|
773
|
-
let message = `Tool result${toolName ? `: ${toolName}` : ""} (${isError ? "error" : "ok"})`;
|
|
774
|
-
const text = extractOpenClawTextContent(eventMessage.content);
|
|
775
|
-
if (text) message = `${message} ${truncateString(text, 320)}`;
|
|
776
|
-
return {
|
|
777
|
-
sessionId,
|
|
778
|
-
eventType: "openclaw.tool_result",
|
|
779
|
-
kind: "system",
|
|
780
|
-
timestamp,
|
|
781
|
-
message: toMessage(message),
|
|
782
|
-
raw: buildOpenClawRaw(parsed)
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
//#endregion
|
|
789
|
-
//#region ../../packages/parsers/src/gstack.mjs
|
|
790
|
-
function extractGstackProjectSlug(filePath) {
|
|
791
|
-
return filePath.match(/\.gstack\/projects\/([^/]+)\//)?.[1] ?? "unknown-project";
|
|
792
|
-
}
|
|
793
|
-
function gstackFileType(filePath) {
|
|
794
|
-
const base = filePath.split("/").pop() ?? "";
|
|
795
|
-
if (base === "learnings.jsonl") return "learning";
|
|
796
|
-
if (base === "timeline.jsonl") return "timeline";
|
|
797
|
-
if (base === "resources-shown.jsonl") return "resource";
|
|
798
|
-
if (base.endsWith("-reviews.jsonl")) return "review";
|
|
799
|
-
return "unknown";
|
|
800
|
-
}
|
|
801
|
-
function parseGstackLine({ line, filePath }) {
|
|
802
|
-
const parsed = safeJsonParse(line);
|
|
803
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
804
|
-
const projectSlug = extractGstackProjectSlug(filePath);
|
|
805
|
-
const fileType = gstackFileType(filePath);
|
|
806
|
-
const timestamp = parsed.ts ?? parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
807
|
-
const sessionId = projectSlug;
|
|
808
|
-
if (fileType === "learning") {
|
|
809
|
-
const insight = normalizeWhitespace(parsed.insight);
|
|
810
|
-
if (!insight) return null;
|
|
811
|
-
const skill = parsed.skill ?? "unknown";
|
|
812
|
-
const conf = typeof parsed.confidence === "number" ? ` [${parsed.confidence}/10]` : "";
|
|
813
|
-
return {
|
|
814
|
-
sessionId,
|
|
815
|
-
eventType: `gstack.learning.${parsed.type ?? "insight"}`,
|
|
816
|
-
kind: "system",
|
|
817
|
-
timestamp,
|
|
818
|
-
message: toMessage(`[${skill}]${conf} ${insight}`),
|
|
819
|
-
raw: parsed
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
if (fileType === "timeline") {
|
|
823
|
-
const skill = parsed.skill ?? "unknown";
|
|
824
|
-
const event = parsed.event ?? "unknown";
|
|
825
|
-
const branch = parsed.branch ? ` (${parsed.branch})` : "";
|
|
826
|
-
const outcome = parsed.outcome ? ` → ${parsed.outcome}` : "";
|
|
827
|
-
const duration = parsed.duration_s ? ` ${parsed.duration_s}s` : "";
|
|
828
|
-
return {
|
|
829
|
-
sessionId,
|
|
830
|
-
eventType: `gstack.timeline.${event}`,
|
|
831
|
-
kind: "system",
|
|
832
|
-
timestamp,
|
|
833
|
-
message: toMessage(`${skill} ${event}${branch}${outcome}${duration}`),
|
|
834
|
-
raw: parsed
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
if (fileType === "review") {
|
|
838
|
-
const skill = parsed.skill ?? "review";
|
|
839
|
-
const score = parsed.overall_score != null ? ` score=${parsed.overall_score}` : "";
|
|
840
|
-
return {
|
|
841
|
-
sessionId,
|
|
842
|
-
eventType: "gstack.review",
|
|
843
|
-
kind: "system",
|
|
844
|
-
timestamp,
|
|
845
|
-
message: toMessage(`${skill}${parsed.status ? ` [${parsed.status}]` : ""}${score}`),
|
|
846
|
-
raw: parsed
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
if (fileType === "resource") {
|
|
850
|
-
const title = normalizeWhitespace(parsed.title);
|
|
851
|
-
const url = parsed.url ?? "";
|
|
852
|
-
if (!title && !url) return null;
|
|
853
|
-
return {
|
|
854
|
-
sessionId,
|
|
855
|
-
eventType: "gstack.resource",
|
|
856
|
-
kind: "system",
|
|
857
|
-
timestamp,
|
|
858
|
-
message: toMessage(title ? `${title} — ${url}` : url),
|
|
859
|
-
raw: parsed
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
return null;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
//#endregion
|
|
866
|
-
//#region ../../packages/parsers/src/writers/claude.mjs
|
|
867
|
-
function isUuid(value) {
|
|
868
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(value ?? "").trim());
|
|
869
|
-
}
|
|
870
|
-
function normalizeSessionUuid(raw) {
|
|
871
|
-
const value = String(raw ?? "").trim();
|
|
872
|
-
if (isUuid(value)) return value;
|
|
873
|
-
return randomUUID();
|
|
874
|
-
}
|
|
875
|
-
function claudeProjectDirName(cwd) {
|
|
876
|
-
return path.resolve(String(cwd || process.cwd())).replace(/[\\/]/g, "-").replace(/[^A-Za-z0-9._-]/g, "-");
|
|
877
|
-
}
|
|
878
|
-
function claudeSessionFilePath(sessionId, cwd, baseDir) {
|
|
879
|
-
const root = baseDir || expandHome("~/.claude/projects");
|
|
880
|
-
return path.join(root, claudeProjectDirName(cwd), `${sessionId}.jsonl`);
|
|
881
|
-
}
|
|
882
|
-
async function hasLocalClaudeSession(sessionId, cwd = "", baseDir) {
|
|
883
|
-
const id = String(sessionId ?? "").trim();
|
|
884
|
-
if (!id) return false;
|
|
885
|
-
const preferredPath = claudeSessionFilePath(id, cwd || process.cwd(), baseDir);
|
|
886
|
-
try {
|
|
887
|
-
if ((await fs$1.stat(preferredPath)).isFile()) return true;
|
|
888
|
-
} catch {}
|
|
889
|
-
const fg = (await import("fast-glob")).default;
|
|
890
|
-
const root = baseDir || expandHome("~/.claude/projects");
|
|
891
|
-
return (await fg([path.join(root, `**/*${id}.jsonl`)], {
|
|
892
|
-
onlyFiles: true,
|
|
893
|
-
absolute: true,
|
|
894
|
-
unique: true,
|
|
895
|
-
suppressErrors: true,
|
|
896
|
-
followSymbolicLinks: false
|
|
897
|
-
})).some((filePath) => path.basename(filePath, ".jsonl") === id);
|
|
898
|
-
}
|
|
899
|
-
async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
900
|
-
const runCwd = String(cwd || process.cwd());
|
|
901
|
-
const resolvedSessionId = normalizeSessionUuid(sessionId);
|
|
902
|
-
const firstTs = asIso(firstMessageTimestamp(messages));
|
|
903
|
-
const filePath = claudeSessionFilePath(resolvedSessionId, runCwd, baseDir);
|
|
904
|
-
if (await hasLocalClaudeSession(resolvedSessionId, runCwd, baseDir)) return {
|
|
905
|
-
written: false,
|
|
906
|
-
reason: "already_exists",
|
|
907
|
-
filePath,
|
|
908
|
-
sessionId: resolvedSessionId
|
|
909
|
-
};
|
|
910
|
-
try {
|
|
911
|
-
await fs$1.mkdir(path.dirname(filePath), { recursive: true });
|
|
912
|
-
const lines = [];
|
|
913
|
-
let parentUuid = null;
|
|
914
|
-
for (let i = 0; i < (messages?.length ?? 0); i += 1) {
|
|
915
|
-
const message = messages[i];
|
|
916
|
-
const normalizedRole = normalizeRole(message?.role);
|
|
917
|
-
const role = normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : "assistant";
|
|
918
|
-
const rawText = coerceMessageText(message).trim();
|
|
919
|
-
if (!rawText) continue;
|
|
920
|
-
const text = normalizedRole === "system" ? `[system] ${rawText}` : rawText;
|
|
921
|
-
const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? new Date(new Date(firstTs).getTime() + i * 1e3).toISOString());
|
|
922
|
-
const entryUuid = randomUUID();
|
|
923
|
-
lines.push(JSON.stringify({
|
|
924
|
-
parentUuid,
|
|
925
|
-
isSidechain: false,
|
|
926
|
-
userType: "external",
|
|
927
|
-
cwd: runCwd,
|
|
928
|
-
sessionId: resolvedSessionId,
|
|
929
|
-
version: "adapter",
|
|
930
|
-
gitBranch: "",
|
|
931
|
-
type: role,
|
|
932
|
-
message: {
|
|
933
|
-
role,
|
|
934
|
-
content: text
|
|
935
|
-
},
|
|
936
|
-
timestamp: ts,
|
|
937
|
-
uuid: entryUuid
|
|
938
|
-
}));
|
|
939
|
-
parentUuid = entryUuid;
|
|
940
|
-
}
|
|
941
|
-
if (lines.length === 0) {
|
|
942
|
-
const entryUuid = randomUUID();
|
|
943
|
-
lines.push(JSON.stringify({
|
|
944
|
-
parentUuid: null,
|
|
945
|
-
isSidechain: false,
|
|
946
|
-
userType: "external",
|
|
947
|
-
cwd: runCwd,
|
|
948
|
-
sessionId: resolvedSessionId,
|
|
949
|
-
version: "adapter",
|
|
950
|
-
gitBranch: "",
|
|
951
|
-
type: "assistant",
|
|
952
|
-
message: {
|
|
953
|
-
role: "assistant",
|
|
954
|
-
content: "[system] Session restored from UltraContext with no user/assistant messages."
|
|
955
|
-
},
|
|
956
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
957
|
-
uuid: entryUuid
|
|
958
|
-
}));
|
|
959
|
-
}
|
|
960
|
-
await fs$1.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
|
|
961
|
-
return {
|
|
962
|
-
written: true,
|
|
963
|
-
reason: "created",
|
|
964
|
-
filePath,
|
|
965
|
-
sessionId: resolvedSessionId
|
|
966
|
-
};
|
|
967
|
-
} catch (error) {
|
|
968
|
-
return {
|
|
969
|
-
written: false,
|
|
970
|
-
reason: "write_failed",
|
|
971
|
-
filePath,
|
|
972
|
-
sessionId: resolvedSessionId,
|
|
973
|
-
error: error instanceof Error ? error.message : String(error)
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
//#endregion
|
|
979
|
-
//#region ../../packages/parsers/src/writers/codex.mjs
|
|
980
|
-
function buildEventMsgLine(message, fallbackTs) {
|
|
981
|
-
const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? fallbackTs);
|
|
982
|
-
const role = normalizeRole(message?.role);
|
|
983
|
-
const text = coerceMessageText(message).trim();
|
|
984
|
-
if (!text) return null;
|
|
985
|
-
if (role === "user") return {
|
|
986
|
-
timestamp: ts,
|
|
987
|
-
type: "event_msg",
|
|
988
|
-
payload: {
|
|
989
|
-
type: "user_message",
|
|
990
|
-
message: text,
|
|
991
|
-
images: [],
|
|
992
|
-
local_images: [],
|
|
993
|
-
text_elements: []
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
|
-
if (role === "assistant") return {
|
|
997
|
-
timestamp: ts,
|
|
998
|
-
type: "event_msg",
|
|
999
|
-
payload: {
|
|
1000
|
-
type: "agent_message",
|
|
1001
|
-
message: text
|
|
1002
|
-
}
|
|
1003
|
-
};
|
|
1004
|
-
return {
|
|
1005
|
-
timestamp: ts,
|
|
1006
|
-
type: "event_msg",
|
|
1007
|
-
payload: {
|
|
1008
|
-
type: "agent_message",
|
|
1009
|
-
message: `[system] ${text}`
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
function sessionFilePath(sessionId, firstTimestamp, baseDir) {
|
|
1014
|
-
const iso = asIso(firstTimestamp);
|
|
1015
|
-
const [year, month, day] = iso.slice(0, 10).split("-");
|
|
1016
|
-
const stamp = iso.replace(/\.\d{3}Z$/, "").replace(/:/g, "-").replace("Z", "");
|
|
1017
|
-
const root = baseDir || expandHome("~/.codex/sessions");
|
|
1018
|
-
const fileName = `rollout-${stamp}-${sessionId}.jsonl`;
|
|
1019
|
-
return path.join(root, year, month, day, fileName);
|
|
1020
|
-
}
|
|
1021
|
-
async function hasLocalCodexSession(sessionId, baseDir) {
|
|
1022
|
-
const id = String(sessionId ?? "").trim();
|
|
1023
|
-
if (!id) return false;
|
|
1024
|
-
const fg = (await import("fast-glob")).default;
|
|
1025
|
-
const root = baseDir || expandHome("~/.codex/sessions");
|
|
1026
|
-
return (await fg([path.join(root, `**/*${id}*.jsonl`)], {
|
|
1027
|
-
onlyFiles: true,
|
|
1028
|
-
absolute: true,
|
|
1029
|
-
unique: true,
|
|
1030
|
-
suppressErrors: true,
|
|
1031
|
-
followSymbolicLinks: false
|
|
1032
|
-
})).some((filePath) => filePath.includes(id));
|
|
1033
|
-
}
|
|
1034
|
-
async function writeCodexSession({ sessionId, cwd, messages, baseDir }) {
|
|
1035
|
-
const id = String(sessionId ?? "").trim();
|
|
1036
|
-
if (!id) return {
|
|
1037
|
-
written: false,
|
|
1038
|
-
reason: "missing_session_id",
|
|
1039
|
-
filePath: ""
|
|
1040
|
-
};
|
|
1041
|
-
if (await hasLocalCodexSession(id, baseDir)) return {
|
|
1042
|
-
written: false,
|
|
1043
|
-
reason: "already_exists",
|
|
1044
|
-
filePath: ""
|
|
1045
|
-
};
|
|
1046
|
-
const firstTs = asIso(firstMessageTimestamp(messages));
|
|
1047
|
-
const filePath = sessionFilePath(id, firstTs, baseDir);
|
|
1048
|
-
try {
|
|
1049
|
-
await fs$1.mkdir(path.dirname(filePath), { recursive: true });
|
|
1050
|
-
const lines = [];
|
|
1051
|
-
lines.push(JSON.stringify({
|
|
1052
|
-
timestamp: firstTs,
|
|
1053
|
-
type: "session_meta",
|
|
1054
|
-
payload: {
|
|
1055
|
-
id,
|
|
1056
|
-
timestamp: firstTs,
|
|
1057
|
-
cwd: cwd || process.cwd(),
|
|
1058
|
-
originator: "ultracontext_daemon",
|
|
1059
|
-
cli_version: "restored",
|
|
1060
|
-
source: "cli",
|
|
1061
|
-
model_provider: "openai"
|
|
1062
|
-
}
|
|
1063
|
-
}));
|
|
1064
|
-
let emitted = 0;
|
|
1065
|
-
for (let i = 0; i < (messages?.length ?? 0); i += 1) {
|
|
1066
|
-
const fallbackTs = new Date(new Date(firstTs).getTime() + i * 1e3).toISOString();
|
|
1067
|
-
const line = buildEventMsgLine(messages[i], fallbackTs);
|
|
1068
|
-
if (!line) continue;
|
|
1069
|
-
lines.push(JSON.stringify(line));
|
|
1070
|
-
emitted += 1;
|
|
1071
|
-
}
|
|
1072
|
-
if (emitted === 0) lines.push(JSON.stringify({
|
|
1073
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1074
|
-
type: "event_msg",
|
|
1075
|
-
payload: {
|
|
1076
|
-
type: "agent_message",
|
|
1077
|
-
message: "[system] Session restored from UltraContext with no user/assistant messages."
|
|
1078
|
-
}
|
|
1079
|
-
}));
|
|
1080
|
-
await fs$1.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
|
|
1081
|
-
return {
|
|
1082
|
-
written: true,
|
|
1083
|
-
reason: "created",
|
|
1084
|
-
filePath
|
|
1085
|
-
};
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
return {
|
|
1088
|
-
written: false,
|
|
1089
|
-
reason: "write_failed",
|
|
1090
|
-
filePath,
|
|
1091
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
185
|
//#endregion
|
|
1097
186
|
//#region ../sync/src/daemon.mjs
|
|
1098
187
|
const LOG_LEVELS = {
|
|
@@ -1173,7 +262,9 @@ async function writeStatusJson(cfg, stats, state, runtime) {
|
|
|
1173
262
|
recentLogs: state.recentLogs.slice(-240),
|
|
1174
263
|
config: {
|
|
1175
264
|
bootstrapMode: cfg.bootstrapMode,
|
|
1176
|
-
claudeIncludeSubagents: cfg.claudeIncludeSubagents
|
|
265
|
+
claudeIncludeSubagents: cfg.claudeIncludeSubagents,
|
|
266
|
+
captureAgents: cfg.captureAgents,
|
|
267
|
+
projectPaths: cfg.projectPaths
|
|
1177
268
|
}
|
|
1178
269
|
};
|
|
1179
270
|
const tmp = STATUS_FILE + ".tmp";
|
|
@@ -1246,6 +337,8 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1246
337
|
bootstrapMode: normalizeBootstrapModeWithPrompt(process$1.env.DAEMON_BOOTSTRAP_MODE ?? "prompt") || "prompt",
|
|
1247
338
|
bootstrapReset: boolFromEnv(process$1.env.DAEMON_BOOTSTRAP_RESET, false),
|
|
1248
339
|
claudeIncludeSubagents: boolFromEnv(process$1.env.CLAUDE_INCLUDE_SUBAGENTS, false),
|
|
340
|
+
captureAgents: normalizeCaptureAgents(),
|
|
341
|
+
projectPaths: normalizeProjectPaths(),
|
|
1249
342
|
cleanupEveryCycles: Math.max(toInt(process$1.env.DAEMON_STORE_CLEANUP_CYCLES, 20), 1)
|
|
1250
343
|
};
|
|
1251
344
|
const stats = {
|
|
@@ -1272,7 +365,8 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1272
365
|
sources: null,
|
|
1273
366
|
ingestMode: "all",
|
|
1274
367
|
daemonRunning: false,
|
|
1275
|
-
lockHandle: null
|
|
368
|
+
lockHandle: null,
|
|
369
|
+
projectPathCache: /* @__PURE__ */ new Map()
|
|
1276
370
|
};
|
|
1277
371
|
function isBenignStdioError(error) {
|
|
1278
372
|
const code = String(error?.code ?? "");
|
|
@@ -1350,7 +444,7 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1350
444
|
return "";
|
|
1351
445
|
}
|
|
1352
446
|
function pushRecentLog(level, message, data) {
|
|
1353
|
-
let line = String(message ?? "");
|
|
447
|
+
let line = String(message ?? "").replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ");
|
|
1354
448
|
if (line.startsWith("Appended event to session context")) line = "context append";
|
|
1355
449
|
if (line.startsWith("Context created")) line = "Context created";
|
|
1356
450
|
if (line.startsWith("Context created without metadata fallback")) line = "Context created (fallback)";
|
|
@@ -1428,7 +522,9 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1428
522
|
function serializeConfigPrefs() {
|
|
1429
523
|
return {
|
|
1430
524
|
bootstrapMode: normalizeBootstrapModeWithPrompt(cfg.bootstrapMode) || "prompt",
|
|
1431
|
-
claudeIncludeSubagents: Boolean(cfg.claudeIncludeSubagents)
|
|
525
|
+
claudeIncludeSubagents: Boolean(cfg.claudeIncludeSubagents),
|
|
526
|
+
captureAgents: normalizeCaptureAgents(cfg.captureAgents),
|
|
527
|
+
projectPaths: normalizeProjectPaths(cfg.projectPaths)
|
|
1432
528
|
};
|
|
1433
529
|
}
|
|
1434
530
|
async function persistConfigPrefsToFile(targetFile = cfg.configFile) {
|
|
@@ -1503,12 +599,25 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1503
599
|
}
|
|
1504
600
|
function applyConfigPrefs(prefs) {
|
|
1505
601
|
if (!prefs || typeof prefs !== "object") return;
|
|
1506
|
-
for (const field of [
|
|
602
|
+
for (const field of [
|
|
603
|
+
"bootstrapMode",
|
|
604
|
+
"claudeIncludeSubagents",
|
|
605
|
+
"captureAgents",
|
|
606
|
+
"projectPaths"
|
|
607
|
+
]) {
|
|
1507
608
|
if (!(field in prefs)) continue;
|
|
1508
609
|
if (field === "bootstrapMode") {
|
|
1509
610
|
cfg.bootstrapMode = normalizeBootstrapModeWithPrompt(prefs.bootstrapMode) || "prompt";
|
|
1510
611
|
continue;
|
|
1511
612
|
}
|
|
613
|
+
if (field === "captureAgents") {
|
|
614
|
+
cfg.captureAgents = normalizeCaptureAgents(prefs.captureAgents);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (field === "projectPaths") {
|
|
618
|
+
cfg.projectPaths = normalizeProjectPaths(prefs.projectPaths);
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
1512
621
|
cfg[field] = Boolean(prefs[field]);
|
|
1513
622
|
}
|
|
1514
623
|
}
|
|
@@ -1518,10 +627,12 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1518
627
|
const before = serializeConfigPrefs();
|
|
1519
628
|
applyConfigPrefs(data);
|
|
1520
629
|
const after = serializeConfigPrefs();
|
|
1521
|
-
if (before.claudeIncludeSubagents !== after.claudeIncludeSubagents) applyRuntimeSources(buildSources());
|
|
630
|
+
if (before.claudeIncludeSubagents !== after.claudeIncludeSubagents || JSON.stringify(before.captureAgents) !== JSON.stringify(after.captureAgents)) applyRuntimeSources(buildSources());
|
|
1522
631
|
if (JSON.stringify(before) !== JSON.stringify(after)) log("info", "Reloaded config from config.json", {
|
|
1523
632
|
claude_subagents: after.claudeIncludeSubagents ? "on" : "off",
|
|
1524
|
-
bootstrap_mode: after.bootstrapMode
|
|
633
|
+
bootstrap_mode: after.bootstrapMode,
|
|
634
|
+
capture_agents: after.captureAgents.join(","),
|
|
635
|
+
project_paths: after.projectPaths.length
|
|
1525
636
|
});
|
|
1526
637
|
if (data.bootstrapReset) {
|
|
1527
638
|
cfg.bootstrapReset = true;
|
|
@@ -1542,13 +653,13 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1542
653
|
const claudeGlob = expandHome(process$1.env.CLAUDE_GLOB ?? "~/.claude/projects/**/*.jsonl");
|
|
1543
654
|
const openclawGlob = expandHome(process$1.env.OPENCLAW_GLOB ?? "~/.openclaw/agents/*/sessions/**/*.jsonl");
|
|
1544
655
|
const sources = [];
|
|
1545
|
-
if (boolFromEnv(process$1.env.INGEST_CODEX, true)) sources.push({
|
|
656
|
+
if (boolFromEnv(process$1.env.INGEST_CODEX, true) && isPrimaryAgentSourceEnabled("codex", cfg.captureAgents)) sources.push({
|
|
1546
657
|
name: "codex",
|
|
1547
658
|
enabled: true,
|
|
1548
659
|
globs: [codexGlob],
|
|
1549
660
|
parseLine: parseCodexLine
|
|
1550
661
|
});
|
|
1551
|
-
if (boolFromEnv(process$1.env.INGEST_CLAUDE, true)) sources.push({
|
|
662
|
+
if (boolFromEnv(process$1.env.INGEST_CLAUDE, true) && isPrimaryAgentSourceEnabled("claude", cfg.captureAgents)) sources.push({
|
|
1552
663
|
name: "claude",
|
|
1553
664
|
enabled: true,
|
|
1554
665
|
globs: [claudeGlob],
|
|
@@ -1561,6 +672,20 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1561
672
|
globs: [openclawGlob],
|
|
1562
673
|
parseLine: parseOpenClawLine
|
|
1563
674
|
});
|
|
675
|
+
const cursorGlob = expandHome(process$1.env.CURSOR_GLOB ?? "~/.cursor/projects/**/*.jsonl");
|
|
676
|
+
if (boolFromEnv(process$1.env.INGEST_CURSOR, true) && isPrimaryAgentSourceEnabled("cursor", cfg.captureAgents)) sources.push({
|
|
677
|
+
name: "cursor",
|
|
678
|
+
enabled: true,
|
|
679
|
+
globs: [cursorGlob],
|
|
680
|
+
parseLine: parseCursorLine
|
|
681
|
+
});
|
|
682
|
+
const geminiGlob = expandHome(process$1.env.GEMINI_GLOB ?? "~/.gemini/tmp/*/chats/session-*.json");
|
|
683
|
+
if (boolFromEnv(process$1.env.INGEST_GEMINI, true)) sources.push({
|
|
684
|
+
name: "gemini",
|
|
685
|
+
enabled: true,
|
|
686
|
+
globs: [geminiGlob],
|
|
687
|
+
parseFile: parseGeminiFile
|
|
688
|
+
});
|
|
1564
689
|
const gstackGlob = expandHome(process$1.env.GSTACK_GLOB ?? "~/.gstack/projects/**/*.jsonl");
|
|
1565
690
|
if (boolFromEnv(process$1.env.INGEST_GSTACK, true)) sources.push({
|
|
1566
691
|
name: "gstack",
|
|
@@ -1580,6 +705,64 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1580
705
|
ignore: source.ignoreGlobs ?? []
|
|
1581
706
|
});
|
|
1582
707
|
}
|
|
708
|
+
function extractProjectPathFromNormalized(normalized) {
|
|
709
|
+
const candidates = [normalized?.raw?.payload?.cwd, normalized?.raw?.cwd];
|
|
710
|
+
for (const candidate of candidates) {
|
|
711
|
+
const value = String(candidate ?? "").trim();
|
|
712
|
+
if (value) return path.resolve(value);
|
|
713
|
+
}
|
|
714
|
+
return "";
|
|
715
|
+
}
|
|
716
|
+
async function discoverProjectPathFromFileHead(source, filePath) {
|
|
717
|
+
const fromFilePath = extractProjectPathFromFile(filePath);
|
|
718
|
+
if (fromFilePath) return path.resolve(fromFilePath);
|
|
719
|
+
if (!source.parseLine) return "";
|
|
720
|
+
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
|
|
721
|
+
const rl = readline.createInterface({
|
|
722
|
+
input: stream,
|
|
723
|
+
crlfDelay: Infinity
|
|
724
|
+
});
|
|
725
|
+
let scanned = 0;
|
|
726
|
+
try {
|
|
727
|
+
for await (const line of rl) {
|
|
728
|
+
if (scanned++ >= 32) break;
|
|
729
|
+
if (!line.trim()) continue;
|
|
730
|
+
const projectPath = extractProjectPathFromNormalized(source.parseLine({
|
|
731
|
+
line,
|
|
732
|
+
filePath
|
|
733
|
+
}));
|
|
734
|
+
if (projectPath) return projectPath;
|
|
735
|
+
}
|
|
736
|
+
} catch {
|
|
737
|
+
return "";
|
|
738
|
+
} finally {
|
|
739
|
+
rl.close();
|
|
740
|
+
stream.destroy();
|
|
741
|
+
}
|
|
742
|
+
return "";
|
|
743
|
+
}
|
|
744
|
+
const PROJECT_PATH_CACHE_LIMIT = 5e3;
|
|
745
|
+
function rememberProjectPath(cacheKey, projectPath) {
|
|
746
|
+
const cache = runtime.projectPathCache;
|
|
747
|
+
if (cache.has(cacheKey)) cache.delete(cacheKey);
|
|
748
|
+
cache.set(cacheKey, projectPath);
|
|
749
|
+
if (cache.size > PROJECT_PATH_CACHE_LIMIT) {
|
|
750
|
+
const oldest = cache.keys().next().value;
|
|
751
|
+
cache.delete(oldest);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async function resolveSourceFileProjectPath({ source, filePath, fileId }) {
|
|
755
|
+
const cacheKey = `${source.name}:${fileId}`;
|
|
756
|
+
if (runtime.projectPathCache.has(cacheKey)) {
|
|
757
|
+
const cached = runtime.projectPathCache.get(cacheKey);
|
|
758
|
+
runtime.projectPathCache.delete(cacheKey);
|
|
759
|
+
runtime.projectPathCache.set(cacheKey, cached);
|
|
760
|
+
return cached || "";
|
|
761
|
+
}
|
|
762
|
+
const projectPath = await discoverProjectPathFromFileHead(source, filePath);
|
|
763
|
+
rememberProjectPath(cacheKey, projectPath || "");
|
|
764
|
+
return projectPath || "";
|
|
765
|
+
}
|
|
1583
766
|
function bootstrapStateStoreKey(sources) {
|
|
1584
767
|
return createBootstrapStateKey({
|
|
1585
768
|
host: cfg.host,
|
|
@@ -1612,7 +795,11 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1612
795
|
if (shouldStop()) break;
|
|
1613
796
|
try {
|
|
1614
797
|
const stat = await fs$1.stat(filePath);
|
|
1615
|
-
|
|
798
|
+
const fileId = `${stat.dev}:${stat.ino}`;
|
|
799
|
+
if (source.parseFile) {
|
|
800
|
+
const contents = await fs$1.readFile(filePath, "utf8");
|
|
801
|
+
store.setOffset(offsetStoreKey(source.name, fileId), sha256(contents));
|
|
802
|
+
} else store.setOffset(offsetStoreKey(source.name, fileId), stat.size);
|
|
1616
803
|
} catch {}
|
|
1617
804
|
}
|
|
1618
805
|
}
|
|
@@ -1764,7 +951,7 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1764
951
|
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
|
|
1765
952
|
return results;
|
|
1766
953
|
}
|
|
1767
|
-
async function appendBulkToUltraContext({ store, uc, sourceName, events, filePath }) {
|
|
954
|
+
async function appendBulkToUltraContext({ store, uc, sourceName, events, filePath, projectPath = "" }) {
|
|
1768
955
|
const bySession = /* @__PURE__ */ new Map();
|
|
1769
956
|
for (const ev of events) {
|
|
1770
957
|
const key = ev.normalized.sessionId;
|
|
@@ -1772,7 +959,6 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1772
959
|
bySession.get(key).push(ev);
|
|
1773
960
|
}
|
|
1774
961
|
const sessionEntries = [...bySession.entries()];
|
|
1775
|
-
const projectPath = extractProjectPathFromFile(filePath);
|
|
1776
962
|
const contextIds = /* @__PURE__ */ new Map();
|
|
1777
963
|
for (const [sessionId, sessionEvents] of sessionEntries) {
|
|
1778
964
|
const contextMeta = {
|
|
@@ -1782,7 +968,20 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1782
968
|
session_id: sessionId,
|
|
1783
969
|
started_at: sessionEvents[0].normalized.timestamp
|
|
1784
970
|
};
|
|
1785
|
-
|
|
971
|
+
const sessionProjectPath = sessionEvents.find((event) => event.projectPath)?.projectPath || projectPath;
|
|
972
|
+
if (sessionProjectPath) contextMeta.project_path = sessionProjectPath;
|
|
973
|
+
const isRealUserEvent = (ev) => {
|
|
974
|
+
if (ev.normalized.kind !== "user") return false;
|
|
975
|
+
const et = ev.normalized.eventType ?? "";
|
|
976
|
+
const msg = ev.normalized.message ?? "";
|
|
977
|
+
if (et === "response_item.message") return false;
|
|
978
|
+
if (msg.startsWith("A new session was started")) return false;
|
|
979
|
+
if (msg.startsWith("[result]")) return false;
|
|
980
|
+
if (msg.startsWith("<")) return false;
|
|
981
|
+
return true;
|
|
982
|
+
};
|
|
983
|
+
const firstUserEvent = sessionEvents.find(isRealUserEvent) ?? sessionEvents.find((ev) => ev.normalized.kind === "user");
|
|
984
|
+
if (firstUserEvent?.normalized?.message) contextMeta.title = firstUserEvent.normalized.message.replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ").trim().slice(0, 120);
|
|
1786
985
|
const ctxId = await getOrCreateContext(store, uc, sessionContextStoreKey(sourceName, sessionId), contextMeta, sourceName);
|
|
1787
986
|
contextIds.set(sessionId, ctxId);
|
|
1788
987
|
}
|
|
@@ -1821,12 +1020,13 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1821
1020
|
lastSessionId: sessionId,
|
|
1822
1021
|
lastAt: Date.now()
|
|
1823
1022
|
});
|
|
1824
|
-
if (cfg.logAppends)
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1023
|
+
if (cfg.logAppends) for (const { normalized } of sessionEvents) {
|
|
1024
|
+
const msg = (normalized.message ?? "").replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ").trim().slice(0, 80);
|
|
1025
|
+
log("info", `[${normalized.eventType}] ${msg}`, {
|
|
1026
|
+
source: sourceName,
|
|
1027
|
+
session_id: sessionId
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1830
1030
|
});
|
|
1831
1031
|
}
|
|
1832
1032
|
async function readNewLines(filePath, offset) {
|
|
@@ -1881,6 +1081,64 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1881
1081
|
bumpSourceStat(source.name, "filesScanned");
|
|
1882
1082
|
const fileId = `${stat.dev}:${stat.ino}`;
|
|
1883
1083
|
const offsetKey = offsetStoreKey(source.name, fileId);
|
|
1084
|
+
const fileProjectPath = await resolveSourceFileProjectPath({
|
|
1085
|
+
source,
|
|
1086
|
+
filePath,
|
|
1087
|
+
fileId
|
|
1088
|
+
});
|
|
1089
|
+
if (!matchesConfiguredProjectPath(cfg.projectPaths, fileProjectPath)) return;
|
|
1090
|
+
if (source.parseFile) {
|
|
1091
|
+
const fileContents = await fs$1.readFile(filePath, "utf8");
|
|
1092
|
+
const contentHash = sha256(fileContents);
|
|
1093
|
+
if (store.getOffset(offsetKey) === contentHash) return;
|
|
1094
|
+
noteSourceActivity(source.name, {
|
|
1095
|
+
lastFile: filePath,
|
|
1096
|
+
lastAt: Date.now()
|
|
1097
|
+
});
|
|
1098
|
+
const allEvents = source.parseFile({
|
|
1099
|
+
fileContents,
|
|
1100
|
+
filePath
|
|
1101
|
+
});
|
|
1102
|
+
if (!Array.isArray(allEvents) || allEvents.length === 0) return;
|
|
1103
|
+
bumpStat("linesRead", allEvents.length);
|
|
1104
|
+
bumpSourceStat(source.name, "linesRead", allEvents.length);
|
|
1105
|
+
const pendingEvents = [];
|
|
1106
|
+
for (let i = 0; i < allEvents.length; i++) {
|
|
1107
|
+
if (shouldStop()) break;
|
|
1108
|
+
const normalized = allEvents[i];
|
|
1109
|
+
if (!normalized || !normalized.sessionId) continue;
|
|
1110
|
+
if (ingestMode === "last_24h" && !isWithinLast24h(normalized.timestamp)) continue;
|
|
1111
|
+
bumpStat("parsedEvents");
|
|
1112
|
+
bumpSourceStat(source.name, "parsedEvents");
|
|
1113
|
+
noteSourceActivity(source.name, {
|
|
1114
|
+
lastEventType: normalized.eventType,
|
|
1115
|
+
lastSessionId: normalized.sessionId,
|
|
1116
|
+
lastAt: Date.now()
|
|
1117
|
+
});
|
|
1118
|
+
const eventId = sha256(`${source.name}|${cfg.host}|${cfg.userId}|${normalized.sessionId}|${fileId}|${i}`);
|
|
1119
|
+
if (!markEventSeen(store, source.name, eventId)) {
|
|
1120
|
+
bumpStat("deduped");
|
|
1121
|
+
bumpSourceStat(source.name, "deduped");
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
pendingEvents.push({
|
|
1125
|
+
normalized,
|
|
1126
|
+
eventId,
|
|
1127
|
+
lineOffset: i,
|
|
1128
|
+
projectPath: fileProjectPath
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
if (pendingEvents.length > 0) await appendBulkToUltraContext({
|
|
1132
|
+
store,
|
|
1133
|
+
uc,
|
|
1134
|
+
sourceName: source.name,
|
|
1135
|
+
events: pendingEvents,
|
|
1136
|
+
filePath,
|
|
1137
|
+
projectPath: fileProjectPath
|
|
1138
|
+
});
|
|
1139
|
+
store.setOffset(offsetKey, contentHash);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1884
1142
|
const { lines, nextOffset } = await readNewLines(filePath, toInt(store.getOffset(offsetKey), 0));
|
|
1885
1143
|
bumpStat("linesRead", lines.length);
|
|
1886
1144
|
bumpSourceStat(source.name, "linesRead", lines.length);
|
|
@@ -1914,7 +1172,8 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1914
1172
|
pendingEvents.push({
|
|
1915
1173
|
normalized,
|
|
1916
1174
|
eventId,
|
|
1917
|
-
lineOffset
|
|
1175
|
+
lineOffset,
|
|
1176
|
+
projectPath: fileProjectPath
|
|
1918
1177
|
});
|
|
1919
1178
|
}
|
|
1920
1179
|
if (pendingEvents.length > 0) await appendBulkToUltraContext({
|
|
@@ -1922,7 +1181,8 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1922
1181
|
uc,
|
|
1923
1182
|
sourceName: source.name,
|
|
1924
1183
|
events: pendingEvents,
|
|
1925
|
-
filePath
|
|
1184
|
+
filePath,
|
|
1185
|
+
projectPath: fileProjectPath
|
|
1926
1186
|
});
|
|
1927
1187
|
store.setOffset(offsetKey, nextOffset);
|
|
1928
1188
|
} catch (error) {
|
|
@@ -2118,5 +1378,5 @@ if (process.argv.includes("--daemon")) daemonBoot({
|
|
|
2118
1378
|
});
|
|
2119
1379
|
|
|
2120
1380
|
//#endregion
|
|
2121
|
-
export {
|
|
1381
|
+
export { };
|
|
2122
1382
|
//# sourceMappingURL=sdk-sync.mjs.map
|