agenr 3.2.0 → 3.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.
package/dist/cli.js CHANGED
@@ -4,12 +4,6 @@ import {
4
4
  computeProcedureRevisionHash,
5
5
  computeProcedureSourceHash
6
6
  } from "./chunk-ZYADFKX3.js";
7
- import {
8
- deriveOpenClawSessionIdFromFilePath,
9
- openClawTranscriptParser,
10
- parseTuiSessionKey,
11
- readOpenClawSessionsStore
12
- } from "./chunk-ZAX3YSTU.js";
13
7
  import {
14
8
  applyClaimExtractionResultToEntry,
15
9
  backfillEpisodeEmbeddings,
@@ -25,6 +19,7 @@ import {
25
19
  evaluateClaimKeyCompactness,
26
20
  evaluateClaimKeySupport,
27
21
  executeEpisodeIngestPlan,
22
+ formatTargetSelectorFromParams,
28
23
  normalizeGroundingTags,
29
24
  prepareEpisodeIngest,
30
25
  previewClaimKeyExtraction,
@@ -33,7 +28,7 @@ import {
33
28
  tokenizeGroundingText,
34
29
  validateEntriesWithIndexes,
35
30
  validateSupersessionRules
36
- } from "./chunk-TMDNFBBC.js";
31
+ } from "./chunk-M5M65AYP.js";
37
32
  import {
38
33
  DEFAULT_CLAIM_EXTRACTION_CONCURRENCY,
39
34
  DEFAULT_SURGEON_CONTEXT_LIMIT,
@@ -61,6 +56,7 @@ import {
61
56
  buildSurgeonProposalLifecycleRationale,
62
57
  composeEmbeddingText,
63
58
  configFileExists,
59
+ createAllEnabledFeatureFlagConfig,
64
60
  createDatabase,
65
61
  createEmbeddingClient,
66
62
  createLlmClient,
@@ -101,7 +97,7 @@ import {
101
97
  updateEntry,
102
98
  validateTemporalValidityRange,
103
99
  writeConfig
104
- } from "./chunk-FMQTRTWE.js";
100
+ } from "./chunk-Z5X7T4QZ.js";
105
101
  import {
106
102
  compactClaimKey,
107
103
  describeClaimKeyNormalizationFailure,
@@ -230,7 +226,7 @@ function formatUnknownError(error) {
230
226
  }
231
227
 
232
228
  // src/cli/commands/ingest.ts
233
- import path9 from "path";
229
+ import path11 from "path";
234
230
  import * as clack4 from "@clack/prompts";
235
231
 
236
232
  // src/core/ingestion/parser.ts
@@ -2211,8 +2207,8 @@ import path3 from "path";
2211
2207
  var GENERIC_TRANSCRIPT_FILE_PATTERN = /^.+\.jsonl(?:\.(?:reset|deleted)\..+)?$/iu;
2212
2208
  async function discoverTranscriptFiles(targetPath, options = {}) {
2213
2209
  const resolvedTargetPath = path3.resolve(targetPath);
2214
- const stat = await fs2.stat(resolvedTargetPath);
2215
- if (stat.isFile()) {
2210
+ const stat2 = await fs2.stat(resolvedTargetPath);
2211
+ if (stat2.isFile()) {
2216
2212
  return matchesTranscriptFileName(path3.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
2217
2213
  }
2218
2214
  const recursive = options.recursive ?? true;
@@ -2231,6 +2227,1037 @@ function matchesTranscriptFileName(fileName) {
2231
2227
  return GENERIC_TRANSCRIPT_FILE_PATTERN.test(fileName.trim());
2232
2228
  }
2233
2229
 
2230
+ // src/adapters/openclaw/transcript/parser.ts
2231
+ import { createHash as createHash2 } from "crypto";
2232
+ import * as fs4 from "fs/promises";
2233
+
2234
+ // src/adapters/openclaw/session/session-id.ts
2235
+ import path4 from "path";
2236
+ function deriveOpenClawSessionIdFromFilePath(sessionFile, logger) {
2237
+ const normalizedSessionFile = sessionFile.trim();
2238
+ if (normalizedSessionFile.length === 0) {
2239
+ debugLog(logger, "session-id", "cannot derive session id from empty session file path");
2240
+ return void 0;
2241
+ }
2242
+ const fileName = path4.basename(normalizedSessionFile);
2243
+ const sessionId = fileName.replace(/\.jsonl(?:\..*)?$/i, "").trim();
2244
+ debugLog(logger, "session-id", `derived session id "${sessionId || "<empty>"}" from file=${normalizedSessionFile}`);
2245
+ return sessionId.length > 0 ? sessionId : void 0;
2246
+ }
2247
+ function debugLog(logger, subsystem, message) {
2248
+ logger?.debug?.(`[agenr] ${subsystem}: ${message}`);
2249
+ }
2250
+
2251
+ // src/adapters/openclaw/transcript/jsonl.ts
2252
+ function parseJsonObjectLineWithDiagnostics(line, lineNumber = 1) {
2253
+ if (!line || line.trim().length === 0) {
2254
+ return {
2255
+ record: null
2256
+ };
2257
+ }
2258
+ try {
2259
+ const parsed = JSON.parse(line);
2260
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2261
+ return {
2262
+ record: parsed
2263
+ };
2264
+ }
2265
+ return {
2266
+ record: null,
2267
+ diagnostic: {
2268
+ kind: "non_object_record",
2269
+ lineNumber,
2270
+ message: `Skipped non-object JSONL line ${lineNumber}`
2271
+ }
2272
+ };
2273
+ } catch {
2274
+ return {
2275
+ record: null,
2276
+ diagnostic: {
2277
+ kind: "malformed_json",
2278
+ lineNumber,
2279
+ message: `Skipped malformed JSONL line ${lineNumber}`
2280
+ }
2281
+ };
2282
+ }
2283
+ }
2284
+ function parseJsonlLines(raw, onRecord) {
2285
+ const lines = raw.split(/\r?\n/);
2286
+ const diagnostics = [];
2287
+ for (let index = 0; index < lines.length; index += 1) {
2288
+ const line = lines[index]?.trim();
2289
+ if (!line) {
2290
+ continue;
2291
+ }
2292
+ const parsed = parseJsonObjectLineWithDiagnostics(line, index + 1);
2293
+ if (parsed.diagnostic) {
2294
+ diagnostics.push(parsed.diagnostic);
2295
+ continue;
2296
+ }
2297
+ if (parsed.record) {
2298
+ onRecord(parsed.record, index + 1);
2299
+ }
2300
+ }
2301
+ return {
2302
+ diagnostics
2303
+ };
2304
+ }
2305
+
2306
+ // src/adapters/openclaw/transcript/tool-summarization.ts
2307
+ var AGENR_FETCH_TARGET_MAX_CHARS = 80;
2308
+ var DEFAULT_TOOL_RESULT_DROP_NAMES = ["read", "web_fetch", "browser", "screenshot", "snapshot", "canvas", "tts"];
2309
+ var DEFAULT_TOOL_RESULT_KEEP_NAMES = ["web_search", "memory_search", "memory_get", "image"];
2310
+ var DEFAULT_TOOL_RESULT_DROP_NAME_SET = new Set(DEFAULT_TOOL_RESULT_DROP_NAMES);
2311
+ var DEFAULT_TOOL_RESULT_KEEP_NAME_SET = new Set(DEFAULT_TOOL_RESULT_KEEP_NAMES);
2312
+ function asRecord(value) {
2313
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2314
+ }
2315
+ function getString(value) {
2316
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
2317
+ }
2318
+ function truncateInline(value, max) {
2319
+ if (value.length <= max) {
2320
+ return value;
2321
+ }
2322
+ return value.slice(0, max);
2323
+ }
2324
+ function firstStringArgValue(args, max) {
2325
+ for (const value of Object.values(args)) {
2326
+ if (typeof value === "string" && value.trim().length > 0) {
2327
+ return truncateInline(value.trim(), max);
2328
+ }
2329
+ }
2330
+ return void 0;
2331
+ }
2332
+ function extractAgenrStoreEntries(args) {
2333
+ const nestedEntries = Array.isArray(args.entries) ? args.entries.flatMap((entry) => {
2334
+ const record = asRecord(entry);
2335
+ return record ? [record] : [];
2336
+ }) : [];
2337
+ if (nestedEntries.length > 0) {
2338
+ return nestedEntries;
2339
+ }
2340
+ if (getString(args.type) || getString(args.subject) || getString(args.content) || getString(args.claimKey) || getString(args.claim_key) || getString(args.supersedes)) {
2341
+ return [args];
2342
+ }
2343
+ return [];
2344
+ }
2345
+ function summarizeAgenrStoreEntry(entry) {
2346
+ const type = getString(entry.type) ?? "unknown";
2347
+ const subject = getString(entry.subject) ?? "(no subject)";
2348
+ const claimKey = getString(entry.claimKey) ?? getString(entry.claim_key);
2349
+ const claimKeySuffix = claimKey ? ` claim_key=${JSON.stringify(truncateInline(claimKey.trim(), 120))}` : "";
2350
+ return `${type}: "${truncateInline(subject, 60)}"${claimKeySuffix}`;
2351
+ }
2352
+ function toolIdentifier(toolName, args) {
2353
+ const normalizedToolName = toolName.trim().toLowerCase();
2354
+ if (normalizedToolName === "read" || normalizedToolName === "edit" || normalizedToolName === "write") {
2355
+ return getString(args.file_path) ?? getString(args.path) ?? getString(args.file) ?? "(unknown file)";
2356
+ }
2357
+ if (normalizedToolName === "exec") {
2358
+ const command = getString(args.command) ?? getString(args.cmd) ?? "(unknown command)";
2359
+ return truncateInline(command, 100);
2360
+ }
2361
+ if (normalizedToolName === "web_fetch") {
2362
+ return getString(args.url) ?? "(unknown url)";
2363
+ }
2364
+ if (normalizedToolName === "web_search") {
2365
+ return getString(args.query) ?? "(unknown query)";
2366
+ }
2367
+ if (normalizedToolName === "browser") {
2368
+ const action = getString(args.action) ?? "(unknown action)";
2369
+ const targetUrl = getString(args.targetUrl) ?? getString(args.url);
2370
+ return targetUrl ? `${action} ${targetUrl}` : action;
2371
+ }
2372
+ if (normalizedToolName === "agenr_store") {
2373
+ const entries = extractAgenrStoreEntries(args);
2374
+ return `${entries.length} entr${entries.length === 1 ? "y" : "ies"}`;
2375
+ }
2376
+ if (normalizedToolName === "agenr_recall") {
2377
+ const query = getString(args.query) ?? "(no query)";
2378
+ return `"${truncateInline(query, 80)}"`;
2379
+ }
2380
+ if (normalizedToolName === "agenr_fetch") {
2381
+ return formatTargetSelectorFromParams(args, { maxValueChars: AGENR_FETCH_TARGET_MAX_CHARS });
2382
+ }
2383
+ if (normalizedToolName === "message") {
2384
+ const action = getString(args.action) ?? "(unknown action)";
2385
+ const target = getString(args.target) ?? getString(args.to) ?? "(unknown target)";
2386
+ return `${truncateInline(action, 80)} to ${truncateInline(target, 80)}`;
2387
+ }
2388
+ if (normalizedToolName === "sessions_spawn") {
2389
+ return getString(args.label) ?? getString(args.task)?.slice(0, 60) ?? "(unknown task)";
2390
+ }
2391
+ if (normalizedToolName === "image") {
2392
+ return getString(args.image) ?? getString(args.url) ?? getString(args.path) ?? "(unknown image)";
2393
+ }
2394
+ if (normalizedToolName === "canvas") {
2395
+ return getString(args.action) ?? "(unknown action)";
2396
+ }
2397
+ if (normalizedToolName === "tts") {
2398
+ const text2 = getString(args.text) ?? "(unknown text)";
2399
+ return truncateInline(text2, 50);
2400
+ }
2401
+ return firstStringArgValue(args, 80) ?? "(unknown)";
2402
+ }
2403
+ function extractToolCallBlocks(content) {
2404
+ if (!Array.isArray(content)) {
2405
+ return [];
2406
+ }
2407
+ const toolCalls = [];
2408
+ for (const block of content) {
2409
+ const record = asRecord(block);
2410
+ if (!record) {
2411
+ continue;
2412
+ }
2413
+ const type = typeof record.type === "string" ? record.type.trim().toLowerCase() : "";
2414
+ const name = getString(record.name) ?? getString(record.tool) ?? getString(record.tool_name);
2415
+ const args = asRecord(record.arguments) ?? asRecord(record.args) ?? asRecord(record.input) ?? {};
2416
+ const id = getString(record.id) ?? getString(record.toolCallId) ?? getString(record.tool_call_id) ?? getString(record.call_id);
2417
+ if ((type === "toolcall" || type === "tool_call" || type === "tool_use" || type === "tooluse") && name) {
2418
+ toolCalls.push({ name, args, id });
2419
+ continue;
2420
+ }
2421
+ if (!type && name && ("arguments" in record || "args" in record || "input" in record)) {
2422
+ toolCalls.push({ name, args, id });
2423
+ }
2424
+ }
2425
+ return toolCalls;
2426
+ }
2427
+ function summarizeToolCall(call, options) {
2428
+ const normalizedToolName = call.name.trim().toLowerCase();
2429
+ const override = options?.overrides?.[normalizedToolName];
2430
+ if (override) {
2431
+ const summary = override(call);
2432
+ if (summary) {
2433
+ return summary;
2434
+ }
2435
+ }
2436
+ const args = call.args;
2437
+ const filePath = getString(args.file_path) ?? getString(args.path) ?? getString(args.file);
2438
+ if (normalizedToolName === "read") {
2439
+ return `[called Read: ${filePath ?? "(unknown file)"}]`;
2440
+ }
2441
+ if (normalizedToolName === "write") {
2442
+ const content = getString(args.content) ?? getString(args.text) ?? "";
2443
+ return `[called Write: ${filePath ?? "(unknown file)"} - ${content.length} chars]`;
2444
+ }
2445
+ if (normalizedToolName === "edit") {
2446
+ const oldText = getString(args.oldText) ?? getString(args.old_string) ?? "";
2447
+ return `[called Edit: ${filePath ?? "(unknown file)"} - replaced ${oldText.length} chars]`;
2448
+ }
2449
+ if (normalizedToolName === "exec") {
2450
+ const command = getString(args.command) ?? getString(args.cmd) ?? "(unknown command)";
2451
+ return `[called exec: ${truncateInline(command, 200)}]`;
2452
+ }
2453
+ if (normalizedToolName === "web_search") {
2454
+ const query = getString(args.query) ?? "(unknown query)";
2455
+ return `[called web_search: ${truncateInline(query, 200)}]`;
2456
+ }
2457
+ if (normalizedToolName === "web_fetch") {
2458
+ const url = getString(args.url) ?? "(unknown url)";
2459
+ return `[called web_fetch: ${truncateInline(url, 200)}]`;
2460
+ }
2461
+ if (normalizedToolName === "browser") {
2462
+ const action = getString(args.action) ?? "(unknown action)";
2463
+ return `[called browser: ${truncateInline(action, 200)}]`;
2464
+ }
2465
+ if (normalizedToolName === "message") {
2466
+ const action = getString(args.action) ?? "(unknown action)";
2467
+ const target = getString(args.target) ?? getString(args.to) ?? "(unknown target)";
2468
+ return `[called message: ${truncateInline(action, 200)} to ${truncateInline(target, 200)}]`;
2469
+ }
2470
+ if (normalizedToolName === "agenr_store") {
2471
+ const entries = extractAgenrStoreEntries(args);
2472
+ if (entries.length === 0) {
2473
+ return "[attempted brain store: (empty)]";
2474
+ }
2475
+ const summaries = entries.slice(0, 3).map(summarizeAgenrStoreEntry);
2476
+ const countSuffix = entries.length > 3 ? ` (+${entries.length - 3} more)` : "";
2477
+ return `[attempted brain store: ${summaries.join(", ")}${countSuffix}]`;
2478
+ }
2479
+ if (normalizedToolName === "agenr_recall") {
2480
+ const query = getString(args.query) ?? "(no query)";
2481
+ return `[recalled from brain: "${truncateInline(query, 100)}"]`;
2482
+ }
2483
+ if (normalizedToolName === "agenr_fetch") {
2484
+ return `[fetched from brain: ${formatTargetSelectorFromParams(args, { maxValueChars: AGENR_FETCH_TARGET_MAX_CHARS })}]`;
2485
+ }
2486
+ if (normalizedToolName === "sessions_spawn") {
2487
+ const label = getString(args.label);
2488
+ const mode = getString(args.mode) ?? "run";
2489
+ const model = getString(args.model);
2490
+ const modelSuffix = model ? ` model=${model}` : "";
2491
+ if (label) {
2492
+ return `[spawned sub-agent: ${label} (${mode}${modelSuffix})]`;
2493
+ }
2494
+ const task = getString(args.task) ?? "(no task)";
2495
+ return `[spawned sub-agent: ${truncateInline(task, 80)} (${mode}${modelSuffix})]`;
2496
+ }
2497
+ const relevantArgValue = firstStringArgValue(
2498
+ Object.fromEntries(
2499
+ Object.entries(args).filter(
2500
+ ([key]) => !["buffer", "content", "data", "newText", "new_string", "oldText", "old_string"].includes(key) && !(normalizedToolName === "write" && key === "text")
2501
+ )
2502
+ ),
2503
+ 80
2504
+ ) ?? "(no args)";
2505
+ return `[called ${call.name}: ${relevantArgValue}]`;
2506
+ }
2507
+ function toolResultPlaceholder(toolName, args) {
2508
+ const normalizedToolName = toolName.trim().length > 0 ? toolName.trim() : "unknown";
2509
+ const identifier = toolIdentifier(normalizedToolName, args);
2510
+ return `[tool result from ${normalizedToolName}: ${identifier} - filtered]`;
2511
+ }
2512
+ function shouldKeepToolResult(toolName, text2, policy) {
2513
+ const normalizedToolName = (toolName ?? "").trim().toLowerCase();
2514
+ const dropToolNames = policy?.dropToolNames ?? DEFAULT_TOOL_RESULT_DROP_NAME_SET;
2515
+ const keepToolNames = policy?.keepToolNames ?? DEFAULT_TOOL_RESULT_KEEP_NAME_SET;
2516
+ if (normalizedToolName && dropToolNames.has(normalizedToolName)) {
2517
+ return { keep: false };
2518
+ }
2519
+ if (normalizedToolName && keepToolNames.has(normalizedToolName)) {
2520
+ return { keep: true, truncateTo: 2e3 };
2521
+ }
2522
+ if (normalizedToolName === "exec") {
2523
+ if (text2.length < 1e3) {
2524
+ return { keep: true, truncateTo: 2e3 };
2525
+ }
2526
+ if (/(error|failed|fail)/i.test(text2)) {
2527
+ return { keep: true, truncateTo: 2e3 };
2528
+ }
2529
+ return { keep: false };
2530
+ }
2531
+ if (text2.length < 500) {
2532
+ return { keep: true, truncateTo: 2e3 };
2533
+ }
2534
+ return { keep: false };
2535
+ }
2536
+
2537
+ // src/adapters/openclaw/transcript/message-content.ts
2538
+ var TEXT_BLOCK_TYPES = /* @__PURE__ */ new Set(["input_text", "output_text", "text"]);
2539
+ function normalizeWhitespace2(value) {
2540
+ return value.replace(/\s+/g, " ").trim();
2541
+ }
2542
+ function extractTextBlocks(content) {
2543
+ if (typeof content === "string") {
2544
+ const normalized = normalizeWhitespace2(content);
2545
+ return normalized ? [normalized] : [];
2546
+ }
2547
+ if (!Array.isArray(content)) {
2548
+ return [];
2549
+ }
2550
+ const textBlocks = [];
2551
+ let nonTextBlockCount = 0;
2552
+ for (const block of content) {
2553
+ if (typeof block === "string") {
2554
+ const normalized = normalizeWhitespace2(block);
2555
+ if (normalized) {
2556
+ textBlocks.push(normalized);
2557
+ }
2558
+ continue;
2559
+ }
2560
+ const record = asRecord(block);
2561
+ if (!record) {
2562
+ continue;
2563
+ }
2564
+ if (typeof record.text === "string") {
2565
+ const normalized = normalizeWhitespace2(record.text);
2566
+ if (normalized) {
2567
+ textBlocks.push(normalized);
2568
+ }
2569
+ continue;
2570
+ }
2571
+ const type = typeof record.type === "string" ? record.type.trim().toLowerCase() : "";
2572
+ if (typeof record.content === "string" && TEXT_BLOCK_TYPES.has(type)) {
2573
+ const normalized = normalizeWhitespace2(record.content);
2574
+ if (normalized) {
2575
+ textBlocks.push(normalized);
2576
+ }
2577
+ continue;
2578
+ }
2579
+ nonTextBlockCount += 1;
2580
+ }
2581
+ if (textBlocks.length === 0 && nonTextBlockCount > 0) {
2582
+ textBlocks.push(`[non-text content omitted: ${nonTextBlockCount} block${nonTextBlockCount === 1 ? "" : "s"}]`);
2583
+ }
2584
+ return textBlocks;
2585
+ }
2586
+ function extractRawTextBlocks(content) {
2587
+ if (typeof content === "string") {
2588
+ return [content];
2589
+ }
2590
+ if (!Array.isArray(content)) {
2591
+ return [];
2592
+ }
2593
+ const textBlocks = [];
2594
+ for (const block of content) {
2595
+ if (typeof block === "string") {
2596
+ textBlocks.push(block);
2597
+ continue;
2598
+ }
2599
+ const record = asRecord(block);
2600
+ if (!record) {
2601
+ continue;
2602
+ }
2603
+ if (typeof record.text === "string") {
2604
+ textBlocks.push(record.text);
2605
+ continue;
2606
+ }
2607
+ const type = typeof record.type === "string" ? record.type.trim().toLowerCase() : "";
2608
+ if (typeof record.content === "string" && TEXT_BLOCK_TYPES.has(type)) {
2609
+ textBlocks.push(record.content);
2610
+ }
2611
+ }
2612
+ return textBlocks;
2613
+ }
2614
+ function normalizeLabel(value) {
2615
+ return value.trim().toLowerCase().replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
2616
+ }
2617
+ function normalizeMessageText(content) {
2618
+ return normalizeWhitespace2(extractTextBlocks(content).join("\n"));
2619
+ }
2620
+ function normalizeOpenClawRole(value) {
2621
+ if (typeof value !== "string") {
2622
+ return "unknown";
2623
+ }
2624
+ const normalized = value.trim().toLowerCase();
2625
+ if (normalized === "user" || normalized === "human") {
2626
+ return "user";
2627
+ }
2628
+ if (normalized === "assistant" || normalized === "ai" || normalized === "developer") {
2629
+ return "assistant";
2630
+ }
2631
+ if (normalized === "system") {
2632
+ return "system";
2633
+ }
2634
+ if (normalized === "tool" || normalized === "toolresult" || normalized === "tool_result") {
2635
+ return "toolResult";
2636
+ }
2637
+ return "unknown";
2638
+ }
2639
+ function truncateWithMarker(text2, maxChars) {
2640
+ if (text2.length <= maxChars) {
2641
+ return text2;
2642
+ }
2643
+ return `${text2.slice(0, maxChars)}
2644
+ [...truncated]`;
2645
+ }
2646
+ function isPureBase64(text2) {
2647
+ const trimmed = text2.trim();
2648
+ if (trimmed.length < 500) {
2649
+ return false;
2650
+ }
2651
+ if (!/[+/=]/.test(trimmed)) {
2652
+ return false;
2653
+ }
2654
+ return /^[A-Za-z0-9+/=\s]{500,}$/.test(trimmed);
2655
+ }
2656
+ function normalizeSessionLabel(value) {
2657
+ const normalized = normalizeLabel(value);
2658
+ return normalized.length > 0 ? normalized : void 0;
2659
+ }
2660
+ function extractConversationLabel(content) {
2661
+ const rawTextBlocks = extractRawTextBlocks(content);
2662
+ for (const block of rawTextBlocks) {
2663
+ const matches = block.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi);
2664
+ for (const match of matches) {
2665
+ const candidate = match[1];
2666
+ if (!candidate) {
2667
+ continue;
2668
+ }
2669
+ try {
2670
+ const parsed = JSON.parse(candidate);
2671
+ const record = asRecord(parsed);
2672
+ const conversationLabel = record ? getString(record.conversation_label) : void 0;
2673
+ const normalizedLabel = conversationLabel ? normalizeSessionLabel(conversationLabel) : void 0;
2674
+ if (normalizedLabel) {
2675
+ return normalizedLabel;
2676
+ }
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ }
2681
+ return void 0;
2682
+ }
2683
+ function extractAssistantTextParts(content) {
2684
+ if (typeof content === "string") {
2685
+ const normalized = normalizeWhitespace2(content);
2686
+ return normalized ? [normalized] : [];
2687
+ }
2688
+ if (!Array.isArray(content)) {
2689
+ return [];
2690
+ }
2691
+ const textParts = [];
2692
+ for (const block of content) {
2693
+ if (typeof block === "string") {
2694
+ const normalized = normalizeWhitespace2(block);
2695
+ if (normalized) {
2696
+ textParts.push(normalized);
2697
+ }
2698
+ continue;
2699
+ }
2700
+ const record = asRecord(block);
2701
+ if (!record) {
2702
+ continue;
2703
+ }
2704
+ if (typeof record.text === "string") {
2705
+ const normalized = normalizeWhitespace2(record.text);
2706
+ if (normalized) {
2707
+ textParts.push(normalized);
2708
+ }
2709
+ continue;
2710
+ }
2711
+ const type = typeof record.type === "string" ? record.type.trim().toLowerCase() : "";
2712
+ if (typeof record.content === "string" && TEXT_BLOCK_TYPES.has(type)) {
2713
+ const normalized = normalizeWhitespace2(record.content);
2714
+ if (normalized) {
2715
+ textParts.push(normalized);
2716
+ }
2717
+ }
2718
+ }
2719
+ return textParts;
2720
+ }
2721
+ function pushMessage(messages, role, text2, timestamp2) {
2722
+ messages.push({
2723
+ index: messages.length,
2724
+ role,
2725
+ text: text2,
2726
+ timestamp: timestamp2
2727
+ });
2728
+ }
2729
+
2730
+ // src/adapters/openclaw/transcript/timestamps.ts
2731
+ import * as fs3 from "fs/promises";
2732
+ function parseTimestampValue(value) {
2733
+ if (typeof value === "string" && value.trim().length > 0) {
2734
+ const parsed = new Date(value);
2735
+ if (!Number.isNaN(parsed.getTime())) {
2736
+ return parsed.toISOString();
2737
+ }
2738
+ }
2739
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
2740
+ const milliseconds = value > 1e12 ? value : value * 1e3;
2741
+ const parsed = new Date(milliseconds);
2742
+ if (!Number.isNaN(parsed.getTime())) {
2743
+ return parsed.toISOString();
2744
+ }
2745
+ }
2746
+ return void 0;
2747
+ }
2748
+ function extractTimestamp(record) {
2749
+ for (const field of ["timestamp", "ts", "created_at", "createdAt", "time", "date"]) {
2750
+ const parsed = parseTimestampValue(record[field]);
2751
+ if (parsed) {
2752
+ return parsed;
2753
+ }
2754
+ }
2755
+ return void 0;
2756
+ }
2757
+ async function getFileMtimeTimestamp(filePath) {
2758
+ try {
2759
+ const stat2 = await fs3.stat(filePath);
2760
+ return parseTimestampValue(stat2.mtime.toISOString());
2761
+ } catch {
2762
+ return void 0;
2763
+ }
2764
+ }
2765
+ async function resolveTimestampFallback(filePath, ...candidates) {
2766
+ for (const candidate of candidates) {
2767
+ const parsed = parseTimestampValue(candidate);
2768
+ if (parsed) {
2769
+ return parsed;
2770
+ }
2771
+ }
2772
+ const fileMtime = await getFileMtimeTimestamp(filePath);
2773
+ if (fileMtime) {
2774
+ return fileMtime;
2775
+ }
2776
+ return (/* @__PURE__ */ new Date()).toISOString();
2777
+ }
2778
+ async function applyMessageTimestampFallbacks(filePath, messages, options) {
2779
+ const fallbackTimestamp = await resolveTimestampFallback(filePath, options?.sessionTimestamp);
2780
+ for (const message of messages) {
2781
+ message.timestamp = parseTimestampValue(message.timestamp) ?? fallbackTimestamp;
2782
+ }
2783
+ return fallbackTimestamp;
2784
+ }
2785
+
2786
+ // src/adapters/openclaw/transcript/parser.ts
2787
+ var SKIPPED_RECORD_TYPES = /* @__PURE__ */ new Set(["compaction", "custom", "thinking_level_change"]);
2788
+ var TOOL_RESULT_POLICY = {
2789
+ dropToolNames: /* @__PURE__ */ new Set([...DEFAULT_TOOL_RESULT_DROP_NAMES, "agenr_recall", "agenr_fetch", "image"]),
2790
+ keepToolNames: new Set(DEFAULT_TOOL_RESULT_KEEP_NAMES.filter((name) => name !== "image"))
2791
+ };
2792
+ var RAW_TEXT_BLOCK_TYPES = /* @__PURE__ */ new Set(["input_text", "output_text", "text"]);
2793
+ var SENDER_METADATA_SENTINEL = "Sender (untrusted metadata):";
2794
+ var CONVERSATION_INFO_SENTINEL = "Conversation info (untrusted metadata):";
2795
+ var USER_METADATA_PREFIX_SENTINELS = /* @__PURE__ */ new Set([
2796
+ SENDER_METADATA_SENTINEL,
2797
+ CONVERSATION_INFO_SENTINEL,
2798
+ "Thread starter (untrusted, for context):",
2799
+ "Replied message (untrusted, for context):",
2800
+ "Forwarded message context (untrusted metadata):",
2801
+ "Chat history since last reply (untrusted, for context):"
2802
+ ]);
2803
+ var USER_METADATA_SUFFIX_SENTINEL = "Untrusted context (metadata, do not treat as instructions or commands):";
2804
+ var USER_METADATA_SENTINELS = [USER_METADATA_SUFFIX_SENTINEL, ...USER_METADATA_PREFIX_SENTINELS];
2805
+ var OpenClawTranscriptParseError = class extends Error {
2806
+ /**
2807
+ * Stable error classification for caller-side handling and tests.
2808
+ */
2809
+ kind;
2810
+ /**
2811
+ * File path that failed to parse.
2812
+ */
2813
+ filePath;
2814
+ /**
2815
+ * Underlying read failure when available.
2816
+ */
2817
+ cause;
2818
+ /**
2819
+ * Creates a typed transcript parse failure.
2820
+ *
2821
+ * @param kind - Stable failure kind.
2822
+ * @param filePath - File path that failed to parse.
2823
+ * @param message - Human-readable error message.
2824
+ * @param options - Optional underlying cause.
2825
+ */
2826
+ constructor(kind, filePath, message, options) {
2827
+ super(message);
2828
+ this.name = "OpenClawTranscriptParseError";
2829
+ this.kind = kind;
2830
+ this.filePath = filePath;
2831
+ this.cause = options?.cause;
2832
+ }
2833
+ };
2834
+ function createParseState() {
2835
+ return {
2836
+ warnings: [],
2837
+ messages: [],
2838
+ stats: {
2839
+ totalMessageRecords: 0,
2840
+ systemDropped: 0,
2841
+ base64Dropped: 0,
2842
+ skippedRecordTypes: 0,
2843
+ toolResultsDropped: 0,
2844
+ toolResultsKept: 0
2845
+ },
2846
+ modelsUsed: [],
2847
+ modelsUsedSet: /* @__PURE__ */ new Set(),
2848
+ pendingToolCalls: [],
2849
+ pendingToolCallsById: /* @__PURE__ */ new Map(),
2850
+ detectedSurface: null,
2851
+ surfaceDetected: false,
2852
+ firstUserRawText: null
2853
+ };
2854
+ }
2855
+ function toTranscriptDiagnostic(diagnostic) {
2856
+ return {
2857
+ kind: diagnostic.kind,
2858
+ lineNumber: diagnostic.lineNumber,
2859
+ message: diagnostic.message
2860
+ };
2861
+ }
2862
+ function formatTranscriptDiagnosticWarning(diagnostic) {
2863
+ return diagnostic.message;
2864
+ }
2865
+ async function readTranscriptFileStrict(filePath) {
2866
+ try {
2867
+ return await fs4.readFile(filePath, "utf8");
2868
+ } catch (error) {
2869
+ if (isFileNotFound(error)) {
2870
+ throw new OpenClawTranscriptParseError("missing_file", filePath, `Transcript file not found: ${filePath}`, { cause: error });
2871
+ }
2872
+ throw new OpenClawTranscriptParseError("unreadable_file", filePath, `Could not read transcript file ${filePath}: ${formatErrorMessage(error)}`, {
2873
+ cause: error
2874
+ });
2875
+ }
2876
+ }
2877
+ function extractRawMessageText(content) {
2878
+ if (typeof content === "string") {
2879
+ return content;
2880
+ }
2881
+ if (!Array.isArray(content)) {
2882
+ return "";
2883
+ }
2884
+ const blocks = [];
2885
+ for (const block of content) {
2886
+ if (typeof block === "string") {
2887
+ blocks.push(block);
2888
+ continue;
2889
+ }
2890
+ const record = asRecord(block);
2891
+ if (!record) {
2892
+ continue;
2893
+ }
2894
+ if (typeof record.text === "string") {
2895
+ blocks.push(record.text);
2896
+ continue;
2897
+ }
2898
+ const type = typeof record.type === "string" ? record.type.trim().toLowerCase() : "";
2899
+ if (typeof record.content === "string" && RAW_TEXT_BLOCK_TYPES.has(type)) {
2900
+ blocks.push(record.content);
2901
+ }
2902
+ }
2903
+ return blocks.join("\n");
2904
+ }
2905
+ function stripOpenClawUserMetadata(content) {
2906
+ const normalizedText = normalizeMessageText(content);
2907
+ if (normalizedText.length === 0) {
2908
+ return normalizedText;
2909
+ }
2910
+ const rawText = extractRawMessageText(content);
2911
+ if (rawText.length === 0 || !USER_METADATA_SENTINELS.some((sentinel) => rawText.includes(sentinel))) {
2912
+ return normalizedText;
2913
+ }
2914
+ return normalizeMessageText(stripMetadataBlocks(rawText));
2915
+ }
2916
+ function stripMetadataBlocks(text2) {
2917
+ const lines = text2.split(/\r?\n/u);
2918
+ let index = 0;
2919
+ while (index < lines.length) {
2920
+ while (index < lines.length && lines[index]?.trim().length === 0) {
2921
+ index += 1;
2922
+ }
2923
+ if (index >= lines.length) {
2924
+ return "";
2925
+ }
2926
+ const line = lines[index]?.trim();
2927
+ if (line === USER_METADATA_SUFFIX_SENTINEL) {
2928
+ return "";
2929
+ }
2930
+ if (!line || !USER_METADATA_PREFIX_SENTINELS.has(line)) {
2931
+ break;
2932
+ }
2933
+ const nextIndex = skipMetadataJsonFence(lines, index);
2934
+ if (nextIndex === index) {
2935
+ break;
2936
+ }
2937
+ index = nextIndex;
2938
+ }
2939
+ const suffixIndex = lines.findIndex((line, lineIndex) => lineIndex >= index && line.trim() === USER_METADATA_SUFFIX_SENTINEL);
2940
+ const body = suffixIndex >= 0 ? lines.slice(index, suffixIndex) : lines.slice(index);
2941
+ return body.join("\n").trim();
2942
+ }
2943
+ function skipMetadataJsonFence(lines, startIndex) {
2944
+ let index = startIndex + 1;
2945
+ while (index < lines.length && lines[index]?.trim().length === 0) {
2946
+ index += 1;
2947
+ }
2948
+ if (index >= lines.length || !/^```(?:json)?\s*$/iu.test(lines[index]?.trim() ?? "")) {
2949
+ return startIndex;
2950
+ }
2951
+ index += 1;
2952
+ while (index < lines.length && !/^```\s*$/u.test(lines[index]?.trim() ?? "")) {
2953
+ index += 1;
2954
+ }
2955
+ if (index >= lines.length) {
2956
+ return startIndex;
2957
+ }
2958
+ index += 1;
2959
+ while (index < lines.length && lines[index]?.trim().length === 0) {
2960
+ index += 1;
2961
+ }
2962
+ return index;
2963
+ }
2964
+ function addModelUsed(state, value) {
2965
+ const modelId = getString(value);
2966
+ if (!modelId || state.modelsUsedSet.has(modelId)) {
2967
+ return;
2968
+ }
2969
+ state.modelsUsedSet.add(modelId);
2970
+ state.modelsUsed.push(modelId);
2971
+ }
2972
+ function setDetectedSurface(state, surface) {
2973
+ if (state.surfaceDetected || !surface) {
2974
+ return;
2975
+ }
2976
+ state.detectedSurface = surface;
2977
+ state.surfaceDetected = true;
2978
+ }
2979
+ function readInboundSurface(record) {
2980
+ const inboundMeta = asRecord(record.inbound_meta);
2981
+ const surface = getString(inboundMeta?.surface)?.trim().toLowerCase();
2982
+ return surface || null;
2983
+ }
2984
+ function extractMetadataPayload(rawText, sentinel) {
2985
+ const lines = rawText.split(/\r?\n/u);
2986
+ for (let index = 0; index < lines.length; index += 1) {
2987
+ if (lines[index]?.trim() !== sentinel) {
2988
+ continue;
2989
+ }
2990
+ let fenceIndex = index + 1;
2991
+ while (fenceIndex < lines.length && lines[fenceIndex]?.trim().length === 0) {
2992
+ fenceIndex += 1;
2993
+ }
2994
+ if (fenceIndex >= lines.length || !/^```(?:json)?\s*$/iu.test(lines[fenceIndex]?.trim() ?? "")) {
2995
+ continue;
2996
+ }
2997
+ fenceIndex += 1;
2998
+ const jsonLines = [];
2999
+ while (fenceIndex < lines.length && !/^```\s*$/u.test(lines[fenceIndex]?.trim() ?? "")) {
3000
+ jsonLines.push(lines[fenceIndex] ?? "");
3001
+ fenceIndex += 1;
3002
+ }
3003
+ if (fenceIndex >= lines.length) {
3004
+ continue;
3005
+ }
3006
+ try {
3007
+ const parsed = JSON.parse(jsonLines.join("\n").trim());
3008
+ return asRecord(parsed);
3009
+ } catch {
3010
+ continue;
3011
+ }
3012
+ }
3013
+ return null;
3014
+ }
3015
+ function mapKnownSurface(value) {
3016
+ if (!value) {
3017
+ return null;
3018
+ }
3019
+ if (value.includes("telegram")) {
3020
+ return "telegram";
3021
+ }
3022
+ if (value.includes("signal")) {
3023
+ return "signal";
3024
+ }
3025
+ if (value.includes("discord")) {
3026
+ return "discord";
3027
+ }
3028
+ if (value.includes("openclaw-tui")) {
3029
+ return "tui";
3030
+ }
3031
+ if (value.includes("gateway-client") || value.includes("openclaw-control-ui") || value.includes("webchat")) {
3032
+ return "webchat";
3033
+ }
3034
+ return null;
3035
+ }
3036
+ function extractSenderSurface(rawText) {
3037
+ const payload = extractMetadataPayload(rawText, SENDER_METADATA_SENTINEL);
3038
+ if (!payload) {
3039
+ return null;
3040
+ }
3041
+ const label = getString(payload.label)?.trim().toLowerCase() ?? getString(payload.id)?.trim().toLowerCase() ?? "";
3042
+ return mapKnownSurface(label);
3043
+ }
3044
+ function extractConversationInfoSurface(rawText) {
3045
+ const payload = extractMetadataPayload(rawText, CONVERSATION_INFO_SENTINEL);
3046
+ if (!payload) {
3047
+ return null;
3048
+ }
3049
+ const senderId = getString(payload.sender_id)?.trim().toLowerCase() ?? "";
3050
+ return mapKnownSurface(senderId);
3051
+ }
3052
+ function inferSurfaceFromContent(firstUserRawText) {
3053
+ const normalized = firstUserRawText?.trim().toLowerCase() ?? "";
3054
+ if (!normalized) {
3055
+ return null;
3056
+ }
3057
+ if (normalized.includes("[subagent context]")) {
3058
+ return "subagent";
3059
+ }
3060
+ if (normalized.includes("heartbeat.md")) {
3061
+ return "heartbeat";
3062
+ }
3063
+ return null;
3064
+ }
3065
+ function resolveToolContext(state, message) {
3066
+ const toolCallId = getString(message.toolCallId) ?? getString(message.tool_call_id) ?? getString(message.call_id) ?? getString(message.id);
3067
+ if (toolCallId && state.pendingToolCallsById.has(toolCallId)) {
3068
+ const context = state.pendingToolCallsById.get(toolCallId) ?? null;
3069
+ state.pendingToolCallsById.delete(toolCallId);
3070
+ if (context) {
3071
+ const queuedIndex = state.pendingToolCalls.findIndex((toolCall) => toolCall.id === toolCallId);
3072
+ if (queuedIndex >= 0) {
3073
+ state.pendingToolCalls.splice(queuedIndex, 1);
3074
+ }
3075
+ }
3076
+ return context;
3077
+ }
3078
+ return state.pendingToolCalls.shift() ?? null;
3079
+ }
3080
+ function handleMessageRecord(state, record, message) {
3081
+ state.stats.totalMessageRecords += 1;
3082
+ const role = normalizeOpenClawRole(message.role);
3083
+ if (!state.surfaceDetected) {
3084
+ setDetectedSurface(state, readInboundSurface(message));
3085
+ }
3086
+ if (!state.surfaceDetected && role === "user") {
3087
+ const rawText = extractRawMessageText(message.content);
3088
+ if (state.firstUserRawText === null) {
3089
+ state.firstUserRawText = rawText;
3090
+ }
3091
+ setDetectedSurface(state, extractSenderSurface(rawText));
3092
+ if (!state.surfaceDetected) {
3093
+ setDetectedSurface(state, extractConversationInfoSurface(rawText));
3094
+ }
3095
+ }
3096
+ if (role === "system") {
3097
+ state.stats.systemDropped += 1;
3098
+ return "known_skip";
3099
+ }
3100
+ const timestamp2 = extractTimestamp(record) ?? extractTimestamp(message);
3101
+ if (role === "user") {
3102
+ const extractedLabel = extractConversationLabel(message.content);
3103
+ if (extractedLabel) {
3104
+ state.sessionLabel = extractedLabel;
3105
+ }
3106
+ const text2 = stripOpenClawUserMetadata(message.content);
3107
+ if (!text2) {
3108
+ return "known_skip";
3109
+ }
3110
+ if (isPureBase64(text2)) {
3111
+ state.stats.base64Dropped += 1;
3112
+ return "known_skip";
3113
+ }
3114
+ pushMessage(state.messages, "user", text2, timestamp2);
3115
+ return "accepted";
3116
+ }
3117
+ if (role === "assistant") {
3118
+ const toolCalls = extractToolCallBlocks(message.content);
3119
+ for (const toolCall of toolCalls) {
3120
+ state.pendingToolCalls.push(toolCall);
3121
+ if (toolCall.id) {
3122
+ state.pendingToolCallsById.set(toolCall.id, toolCall);
3123
+ }
3124
+ }
3125
+ const assistantText = [...extractAssistantTextParts(message.content), ...toolCalls.map((toolCall) => summarizeToolCall(toolCall))].join(" ").trim();
3126
+ addModelUsed(state, message.model);
3127
+ if (!assistantText) {
3128
+ return "known_skip";
3129
+ }
3130
+ if (isPureBase64(assistantText)) {
3131
+ state.stats.base64Dropped += 1;
3132
+ return "known_skip";
3133
+ }
3134
+ pushMessage(state.messages, "assistant", truncateWithMarker(assistantText, 5e3), timestamp2);
3135
+ return "accepted";
3136
+ }
3137
+ if (role !== "toolResult") {
3138
+ return "structurally_invalid";
3139
+ }
3140
+ const toolContext = resolveToolContext(state, message);
3141
+ const toolName = getString(message.name) ?? getString(message.tool) ?? getString(record.name) ?? getString(record.tool) ?? toolContext?.name;
3142
+ const toolArgs = toolContext?.args ?? {};
3143
+ const toolText = normalizeMessageText(message.content);
3144
+ if (!toolText) {
3145
+ return "known_skip";
3146
+ }
3147
+ if (isPureBase64(toolText)) {
3148
+ state.stats.base64Dropped += 1;
3149
+ return "known_skip";
3150
+ }
3151
+ const decision = shouldKeepToolResult(toolName, toolText, TOOL_RESULT_POLICY);
3152
+ if (decision.keep) {
3153
+ state.stats.toolResultsKept += 1;
3154
+ pushMessage(state.messages, "assistant", decision.truncateTo ? truncateWithMarker(toolText, decision.truncateTo) : toolText, timestamp2);
3155
+ return "accepted";
3156
+ }
3157
+ state.stats.toolResultsDropped += 1;
3158
+ pushMessage(state.messages, "assistant", toolResultPlaceholder(toolName ?? "unknown", toolArgs), timestamp2);
3159
+ return "accepted";
3160
+ }
3161
+ function handleRecord(state, record) {
3162
+ if (record.type === "session") {
3163
+ state.sessionId = getString(record.id) ?? state.sessionId;
3164
+ state.sessionTimestamp = extractTimestamp(record) ?? state.sessionTimestamp;
3165
+ state.sessionLabel = normalizeSessionLabel(getString(record.conversation_label) ?? "") ?? state.sessionLabel;
3166
+ state.workingDirectory = getString(record.cwd) ?? state.workingDirectory;
3167
+ addModelUsed(state, record.model);
3168
+ if (!state.surfaceDetected) {
3169
+ setDetectedSurface(state, readInboundSurface(record));
3170
+ }
3171
+ return "accepted";
3172
+ }
3173
+ if (!state.surfaceDetected) {
3174
+ setDetectedSurface(state, readInboundSurface(record));
3175
+ }
3176
+ if (record.type === "model_change") {
3177
+ addModelUsed(state, record.modelId);
3178
+ state.stats.skippedRecordTypes += 1;
3179
+ return "known_skip";
3180
+ }
3181
+ if (typeof record.type === "string" && SKIPPED_RECORD_TYPES.has(record.type)) {
3182
+ state.stats.skippedRecordTypes += 1;
3183
+ return "known_skip";
3184
+ }
3185
+ const message = asRecord(record.message);
3186
+ if (!message) {
3187
+ return "structurally_invalid";
3188
+ }
3189
+ return handleMessageRecord(state, record, message);
3190
+ }
3191
+ function buildFilterWarning(stats) {
3192
+ return `Filtered transcript: ${stats.toolResultsDropped} tool results dropped, ${stats.toolResultsKept} kept, ${stats.systemDropped} system dropped, ${stats.base64Dropped} base64 dropped.`;
3193
+ }
3194
+ function isFileNotFound(error) {
3195
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
3196
+ }
3197
+ function formatErrorMessage(error) {
3198
+ if (error instanceof Error) {
3199
+ return error.message;
3200
+ }
3201
+ return String(error);
3202
+ }
3203
+ var OpenClawTranscriptParser = class {
3204
+ /**
3205
+ * Parses an OpenClaw JSONL transcript file into agenr transcript data.
3206
+ *
3207
+ * @param filePath - Absolute or relative path to the transcript file.
3208
+ * @param options - Optional parser flags for verbose diagnostics.
3209
+ * @returns Parsed transcript messages, warnings, and metadata.
3210
+ */
3211
+ async parseFile(filePath, options) {
3212
+ const raw = await readTranscriptFileStrict(filePath);
3213
+ const verbose = options?.verbose === true;
3214
+ const state = createParseState();
3215
+ const transcriptHash = createHash2("sha256").update(raw).digest("hex");
3216
+ const diagnostics = [];
3217
+ const jsonlResult = parseJsonlLines(raw, (record, lineNumber) => {
3218
+ const outcome = handleRecord(state, record);
3219
+ if (outcome === "structurally_invalid") {
3220
+ diagnostics.push({
3221
+ kind: "structurally_invalid_record",
3222
+ lineNumber,
3223
+ message: `Skipped structurally invalid transcript record on line ${lineNumber}`
3224
+ });
3225
+ }
3226
+ });
3227
+ diagnostics.push(...jsonlResult.diagnostics.map(toTranscriptDiagnostic));
3228
+ state.warnings.push(...diagnostics.map(formatTranscriptDiagnosticWarning));
3229
+ if (!state.surfaceDetected && state.firstUserRawText) {
3230
+ setDetectedSurface(state, inferSurfaceFromContent(state.firstUserRawText));
3231
+ }
3232
+ const fallbackTimestamp = state.messages.length > 0 ? await applyMessageTimestampFallbacks(filePath, state.messages, { sessionTimestamp: state.sessionTimestamp }) : await resolveTimestampFallback(filePath, state.sessionTimestamp);
3233
+ if (verbose) {
3234
+ state.warnings.push(buildFilterWarning(state.stats));
3235
+ }
3236
+ const startedAt = state.sessionTimestamp ?? state.messages[0]?.timestamp ?? fallbackTimestamp;
3237
+ const endedAt = state.messages[state.messages.length - 1]?.timestamp ?? state.sessionTimestamp ?? fallbackTimestamp;
3238
+ const stableSessionId = state.sessionId ?? deriveOpenClawSessionIdFromFilePath(filePath);
3239
+ return {
3240
+ messages: state.messages,
3241
+ warnings: state.warnings,
3242
+ metadata: {
3243
+ sessionId: state.sessionId,
3244
+ sessionLabel: state.sessionLabel,
3245
+ startedAt,
3246
+ endedAt,
3247
+ messageCount: state.messages.length,
3248
+ transcriptHash,
3249
+ modelsUsed: state.modelsUsed.length > 0 ? state.modelsUsed : void 0,
3250
+ reconstructedSurface: state.detectedSurface,
3251
+ surfaceReconstructionSource: state.surfaceDetected ? "reconstructed" : "none",
3252
+ sourceIdentity: stableSessionId ? `openclaw-session:${stableSessionId}` : void 0,
3253
+ sourceIdentityKind: stableSessionId ? "openclaw_session" : void 0,
3254
+ workingDirectory: state.workingDirectory
3255
+ }
3256
+ };
3257
+ }
3258
+ };
3259
+ var openClawTranscriptParser = new OpenClawTranscriptParser();
3260
+
2234
3261
  // src/cli/shared/parse.ts
2235
3262
  import { InvalidArgumentError } from "commander";
2236
3263
  function normalizeOptionalString2(value) {
@@ -2365,13 +3392,13 @@ function timestamp() {
2365
3392
  import { InvalidArgumentError as InvalidArgumentError3, Option as Option2 } from "commander";
2366
3393
 
2367
3394
  // src/cli/commands/ingest-episodes.ts
2368
- import fs4 from "fs/promises";
2369
- import path6 from "path";
3395
+ import fs7 from "fs/promises";
3396
+ import path8 from "path";
2370
3397
  import * as clack2 from "@clack/prompts";
2371
3398
  import { InvalidArgumentError as InvalidArgumentError2, Option } from "commander";
2372
3399
 
2373
3400
  // src/adapters/db/episode-ingest-support.ts
2374
- import path4 from "path";
3401
+ import path5 from "path";
2375
3402
  function createEpisodeIngestSupportPort(executor) {
2376
3403
  return {
2377
3404
  countEntries: async () => countRows(executor, "SELECT COUNT(*) AS count FROM entries"),
@@ -2387,7 +3414,7 @@ async function hasRelevantProvenanceMatch(executor, sampleFiles) {
2387
3414
  if (ingestLogMatches > 0) {
2388
3415
  return true;
2389
3416
  }
2390
- const basenames = Array.from(new Set(sampleFiles.map((filePath) => path4.basename(filePath))));
3417
+ const basenames = Array.from(new Set(sampleFiles.map((filePath) => path5.basename(filePath))));
2391
3418
  const basenameClauses = basenames.map(() => "(source_file = ? OR source_file LIKE ?)").join(" OR ");
2392
3419
  const basenameArgs = basenames.flatMap((basename) => [basename, `%/${basename}`]);
2393
3420
  const entryMatches = await countRows(executor, `SELECT COUNT(*) AS count FROM entries WHERE source_file IS NOT NULL AND (${basenameClauses})`, basenameArgs);
@@ -2410,6 +3437,171 @@ async function countRows(executor, sql, args = []) {
2410
3437
  return Number.isFinite(normalized) ? normalized : 0;
2411
3438
  }
2412
3439
 
3440
+ // src/adapters/openclaw/session/sessions-store-reader.ts
3441
+ import * as fs5 from "fs/promises";
3442
+ import path6 from "path";
3443
+ async function readOpenClawSessionsStore(sessionsDir, logger) {
3444
+ if (sessionsDir.trim().length === 0) {
3445
+ debugLog2(logger, "sessions-store-reader", "skipping sessions.json read because sessionsDir is empty");
3446
+ return [];
3447
+ }
3448
+ const result = await readOpenClawSessionsStoreWithDiagnostics(sessionsDir);
3449
+ for (const diagnostic of result.diagnostics) {
3450
+ debugLog2(logger, "sessions-store-reader", diagnostic.message);
3451
+ }
3452
+ if (result.diagnostics.length === 0) {
3453
+ debugLog2(
3454
+ logger,
3455
+ "sessions-store-reader",
3456
+ `loaded sessions.json entries=${result.entries.length} path=${path6.join(path6.resolve(sessionsDir.trim()), "sessions.json")}`
3457
+ );
3458
+ }
3459
+ return result.entries;
3460
+ }
3461
+ async function readOpenClawSessionsStoreWithDiagnostics(sessionsDir) {
3462
+ const normalizedSessionsDir = sessionsDir.trim();
3463
+ if (normalizedSessionsDir.length === 0) {
3464
+ return {
3465
+ entries: [],
3466
+ diagnostics: []
3467
+ };
3468
+ }
3469
+ const resolvedSessionsDir = path6.resolve(normalizedSessionsDir);
3470
+ const sessionsJsonPath = path6.join(resolvedSessionsDir, "sessions.json");
3471
+ try {
3472
+ const raw = await fs5.readFile(sessionsJsonPath, "utf8");
3473
+ const parsed = JSON.parse(raw);
3474
+ if (!isRecord(parsed)) {
3475
+ return {
3476
+ entries: [],
3477
+ diagnostics: [
3478
+ {
3479
+ kind: "structurally_invalid_file",
3480
+ message: `sessions.json did not contain an object: path=${sessionsJsonPath}`,
3481
+ path: sessionsJsonPath
3482
+ }
3483
+ ]
3484
+ };
3485
+ }
3486
+ const entries = [];
3487
+ for (const [sessionKey, value] of Object.entries(parsed)) {
3488
+ const normalizedSessionKey = sessionKey.trim();
3489
+ if (normalizedSessionKey.length === 0) {
3490
+ continue;
3491
+ }
3492
+ if (!isRecord(value)) {
3493
+ continue;
3494
+ }
3495
+ const sessionId = asTrimmedString(value["sessionId"]);
3496
+ const sessionFile = asTrimmedString(value["sessionFile"]);
3497
+ const origin = isRecord(value["origin"]) ? value["origin"] : void 0;
3498
+ const surface = asTrimmedString(origin?.["surface"]);
3499
+ const provider = asTrimmedString(origin?.["provider"]);
3500
+ const chatType = asTrimmedString(value["chatType"]);
3501
+ const updatedAt = asFiniteNumber(value["updatedAt"]);
3502
+ entries.push({
3503
+ sessionKey: normalizedSessionKey,
3504
+ ...sessionId ? { sessionId } : {},
3505
+ ...sessionFile ? { sessionFile: resolveSessionStorePath(sessionFile, resolvedSessionsDir) } : {},
3506
+ ...surface ? { surface } : {},
3507
+ ...provider ? { provider } : {},
3508
+ ...chatType ? { chatType } : {},
3509
+ ...updatedAt !== void 0 ? { updatedAt } : {}
3510
+ });
3511
+ }
3512
+ return {
3513
+ entries,
3514
+ diagnostics: []
3515
+ };
3516
+ } catch (error) {
3517
+ if (isFileNotFound2(error)) {
3518
+ return {
3519
+ entries: [],
3520
+ diagnostics: [
3521
+ {
3522
+ kind: "missing_file",
3523
+ message: `sessions.json missing at ${sessionsJsonPath}`,
3524
+ path: sessionsJsonPath
3525
+ }
3526
+ ]
3527
+ };
3528
+ }
3529
+ if (error instanceof SyntaxError) {
3530
+ return {
3531
+ entries: [],
3532
+ diagnostics: [
3533
+ {
3534
+ kind: "malformed_json",
3535
+ message: `sessions.json parse failed at ${sessionsJsonPath}: ${error.message}`,
3536
+ path: sessionsJsonPath
3537
+ }
3538
+ ]
3539
+ };
3540
+ }
3541
+ return {
3542
+ entries: [],
3543
+ diagnostics: [
3544
+ {
3545
+ kind: "unreadable_file",
3546
+ message: `sessions.json read failed at ${sessionsJsonPath}: ${formatErrorMessage2(error)}`,
3547
+ path: sessionsJsonPath
3548
+ }
3549
+ ]
3550
+ };
3551
+ }
3552
+ }
3553
+ function resolveSessionStorePath(candidatePath, sessionsDir) {
3554
+ return path6.isAbsolute(candidatePath) ? path6.resolve(candidatePath) : path6.resolve(sessionsDir, candidatePath);
3555
+ }
3556
+ function isRecord(value) {
3557
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3558
+ }
3559
+ function asTrimmedString(value) {
3560
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
3561
+ }
3562
+ function asFiniteNumber(value) {
3563
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3564
+ }
3565
+ function debugLog2(logger, subsystem, message) {
3566
+ logger?.debug?.(`[agenr] ${subsystem}: ${message}`);
3567
+ }
3568
+ function isFileNotFound2(error) {
3569
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
3570
+ }
3571
+ function formatErrorMessage2(error) {
3572
+ if (error instanceof Error) {
3573
+ return error.message;
3574
+ }
3575
+ return String(error);
3576
+ }
3577
+
3578
+ // src/adapters/openclaw/session/tui-lane.ts
3579
+ var TUI_SESSION_KEY_PATTERN = /^agent:([^:]+):([^:]+)$/i;
3580
+ var TUI_UUID_LANE_PATTERN = /^tui[a-z0-9]*-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3581
+ var TUI_UUID_SUFFIX_PATTERN = /-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3582
+ function parseTuiSessionKey(sessionKey) {
3583
+ const normalizedSessionKey = sessionKey.trim();
3584
+ if (normalizedSessionKey.length === 0) {
3585
+ return null;
3586
+ }
3587
+ const match = TUI_SESSION_KEY_PATTERN.exec(normalizedSessionKey);
3588
+ if (!match) {
3589
+ return null;
3590
+ }
3591
+ const [, agentId, instanceLane] = match;
3592
+ const normalizedAgentId = agentId?.trim();
3593
+ const normalizedInstanceLane = instanceLane?.trim();
3594
+ if (!normalizedAgentId || !normalizedInstanceLane || !normalizedInstanceLane.toLowerCase().startsWith("tui")) {
3595
+ return null;
3596
+ }
3597
+ const stableLane = TUI_UUID_LANE_PATTERN.test(normalizedInstanceLane) ? normalizedInstanceLane.replace(TUI_UUID_SUFFIX_PATTERN, "") : normalizedInstanceLane;
3598
+ return {
3599
+ agentId: normalizedAgentId,
3600
+ stableLane,
3601
+ instanceLane: normalizedInstanceLane
3602
+ };
3603
+ }
3604
+
2413
3605
  // src/adapters/openclaw/session/session-registry.ts
2414
3606
  var GENERIC_AGENT_SESSION_KEY_PATTERN = /^agent:([^:]+):/i;
2415
3607
  async function loadOpenClawSessionRegistry(sessionsDir) {
@@ -2476,17 +3668,17 @@ function normalizeNullableString(value) {
2476
3668
  }
2477
3669
 
2478
3670
  // src/adapters/openclaw/session/transcript-files.ts
2479
- import fs3 from "fs/promises";
2480
- import path5 from "path";
3671
+ import fs6 from "fs/promises";
3672
+ import path7 from "path";
2481
3673
  var OPENCLAW_TRANSCRIPT_FILE_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl(?:\.(?:reset|deleted)\..+)?$/i;
2482
3674
  async function discoverOpenClawTranscriptFiles(targetPath) {
2483
- const resolvedTargetPath = path5.resolve(targetPath);
2484
- const stat = await fs3.stat(resolvedTargetPath);
2485
- if (stat.isFile()) {
2486
- return matchesOpenClawTranscriptFile(path5.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
3675
+ const resolvedTargetPath = path7.resolve(targetPath);
3676
+ const stat2 = await fs6.stat(resolvedTargetPath);
3677
+ if (stat2.isFile()) {
3678
+ return matchesOpenClawTranscriptFile(path7.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
2487
3679
  }
2488
- const entries = await fs3.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true });
2489
- return entries.filter((entry) => entry.isFile() && matchesOpenClawTranscriptFile(entry.name)).map((entry) => path5.resolve(entry.parentPath ?? resolvedTargetPath, entry.name)).sort((left, right) => left.localeCompare(right));
3680
+ const entries = await fs6.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true });
3681
+ return entries.filter((entry) => entry.isFile() && matchesOpenClawTranscriptFile(entry.name)).map((entry) => path7.resolve(entry.parentPath ?? resolvedTargetPath, entry.name)).sort((left, right) => left.localeCompare(right));
2490
3682
  }
2491
3683
  var openClawTranscriptFiles = {
2492
3684
  discoverFiles: discoverOpenClawTranscriptFiles
@@ -2533,7 +3725,7 @@ function registerIngestEpisodesCommand(parent) {
2533
3725
  }
2534
3726
  const { provider, modelId } = resolveEpisodeModel(config, commandInput.modelOverride);
2535
3727
  const modelInfo = createEpisodeIngestSummaryModelInfo(provider, modelId);
2536
- const resolvedTargetPath = path6.resolve(normalizedTargetPath);
3728
+ const resolvedTargetPath = path8.resolve(normalizedTargetPath);
2537
3729
  const sessionsDir = await resolveSessionsDirectory(resolvedTargetPath);
2538
3730
  const ports = await createEpisodeIngestPorts(database, sessionsDir);
2539
3731
  const embeddingSetup = resolveEpisodeEmbeddingSetup(config, commandInput);
@@ -2748,8 +3940,8 @@ function createEpisodeIngestSummaryLlm(provider, modelId, apiKey) {
2748
3940
  };
2749
3941
  }
2750
3942
  async function resolveSessionsDirectory(targetPath) {
2751
- const stat = await fs4.stat(targetPath);
2752
- return stat.isFile() ? path6.dirname(targetPath) : targetPath;
3943
+ const stat2 = await fs7.stat(targetPath);
3944
+ return stat2.isFile() ? path8.dirname(targetPath) : targetPath;
2753
3945
  }
2754
3946
  function resolveEpisodeDbPath(config, overridePath) {
2755
3947
  const normalizedOverride = normalizeOptionalString2(overridePath);
@@ -2759,7 +3951,7 @@ function resolveEpisodeDbPath(config, overridePath) {
2759
3951
  if (normalizedOverride === ":memory:" || normalizedOverride.startsWith("file:")) {
2760
3952
  return normalizedOverride;
2761
3953
  }
2762
- return path6.resolve(normalizedOverride);
3954
+ return path8.resolve(normalizedOverride);
2763
3955
  }
2764
3956
  function resolveEpisodeModel(config, overrideRef) {
2765
3957
  const resolved = resolveModel(config, "episode");
@@ -2873,7 +4065,7 @@ function reportEpisodeProgress(completed, total, session) {
2873
4065
  }
2874
4066
  function formatEpisodeProgressLine(completed, total, session) {
2875
4067
  const prefix = completed !== void 0 && total !== void 0 ? `${completed}/${total} ` : "";
2876
- const fileLabel = path6.basename(session.filePath);
4068
+ const fileLabel = path8.basename(session.filePath);
2877
4069
  const details = session.action === "failed" ? `failed (${session.error ?? "unknown error"})` : session.action;
2878
4070
  const usage = session.usage.totalCost > 0 ? ` ${formatCost(session.usage.totalCost)}` : "";
2879
4071
  return `${prefix}${fileLabel}: ${details}${usage}`;
@@ -2917,26 +4109,26 @@ function parseEpisodeIngestConcurrency(value) {
2917
4109
  }
2918
4110
 
2919
4111
  // src/cli/commands/ingest-procedures.ts
2920
- import path8 from "path";
4112
+ import path10 from "path";
2921
4113
  import * as clack3 from "@clack/prompts";
2922
4114
  import "commander";
2923
4115
 
2924
4116
  // src/adapters/files/procedure-files.ts
2925
- import fs5 from "fs/promises";
2926
- import path7 from "path";
4117
+ import fs8 from "fs/promises";
4118
+ import path9 from "path";
2927
4119
  var PROCEDURE_FILE_PATTERN = /^.+\.ya?ml$/iu;
2928
4120
  async function discoverProcedureFiles(targetPath, options = {}) {
2929
- const resolvedTargetPath = path7.resolve(targetPath);
2930
- const stat = await fs5.stat(resolvedTargetPath);
2931
- if (stat.isFile()) {
2932
- return matchesProcedureFileName(path7.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
4121
+ const resolvedTargetPath = path9.resolve(targetPath);
4122
+ const stat2 = await fs8.stat(resolvedTargetPath);
4123
+ if (stat2.isFile()) {
4124
+ return matchesProcedureFileName(path9.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
2933
4125
  }
2934
4126
  const recursive = options.recursive ?? true;
2935
- const entries = recursive ? await fs5.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true }) : await fs5.readdir(resolvedTargetPath, { withFileTypes: true });
2936
- return entries.filter((entry) => entry.isFile() && matchesProcedureFileName(entry.name)).map((entry) => path7.resolve(entry.parentPath ?? resolvedTargetPath, entry.name)).sort((left, right) => left.localeCompare(right));
4127
+ const entries = recursive ? await fs8.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true }) : await fs8.readdir(resolvedTargetPath, { withFileTypes: true });
4128
+ return entries.filter((entry) => entry.isFile() && matchesProcedureFileName(entry.name)).map((entry) => path9.resolve(entry.parentPath ?? resolvedTargetPath, entry.name)).sort((left, right) => left.localeCompare(right));
2937
4129
  }
2938
4130
  async function readProcedureFile(filePath) {
2939
- return fs5.readFile(filePath, "utf-8");
4131
+ return fs8.readFile(filePath, "utf-8");
2940
4132
  }
2941
4133
  var LocalProcedureFiles = class {
2942
4134
  /**
@@ -3271,7 +4463,7 @@ function registerIngestProceduresCommand(parent) {
3271
4463
  try {
3272
4464
  const config = readConfig();
3273
4465
  const dbPath = config.dbPath;
3274
- const resolvedTargetPath = path8.resolve(commandInput.targetPath);
4466
+ const resolvedTargetPath = path10.resolve(commandInput.targetPath);
3275
4467
  database = await createDatabase(dbPath);
3276
4468
  if (commandInput.verbose) {
3277
4469
  clack3.log.step(`Preparing procedure sync for ${resolvedTargetPath}...`);
@@ -3448,11 +4640,11 @@ function registerIngestEntriesCommand(parent) {
3448
4640
  const claimApiKey = claimModel ? resolveLlmApiKey(config, claimModel.provider) : void 0;
3449
4641
  const sharedEmbedding = createEmbeddingClient(resolveEmbeddingApiKey(config), resolveEmbeddingModel(config));
3450
4642
  if (commandInput.verbose) {
3451
- clack4.log.step(`Discovering transcript files in ${path9.resolve(commandInput.targetPath)}...`);
4643
+ clack4.log.step(`Discovering transcript files in ${path11.resolve(commandInput.targetPath)}...`);
3452
4644
  }
3453
4645
  const files = await localTranscriptFiles.discoverFiles(commandInput.targetPath);
3454
4646
  if (files.length === 0) {
3455
- clack4.log.warn(`No transcript files found at ${path9.resolve(commandInput.targetPath)}.`);
4647
+ clack4.log.warn(`No transcript files found at ${path11.resolve(commandInput.targetPath)}.`);
3456
4648
  clack4.outro("Nothing to ingest.");
3457
4649
  return;
3458
4650
  }
@@ -3548,9 +4740,9 @@ function registerIngestEntriesCommand(parent) {
3548
4740
  totals.skippedFiles += 1;
3549
4741
  if (commandInput.verbose) {
3550
4742
  printVerboseFileDetails(result, commandInput, usage);
3551
- clack4.log.step(buildSkippedMessage(path9.basename(result.file)));
4743
+ clack4.log.step(buildSkippedMessage(path11.basename(result.file)));
3552
4744
  } else {
3553
- clack4.log.step(buildSkippedMessage(path9.basename(result.file)));
4745
+ clack4.log.step(buildSkippedMessage(path11.basename(result.file)));
3554
4746
  }
3555
4747
  continue;
3556
4748
  }
@@ -3559,7 +4751,7 @@ function registerIngestEntriesCommand(parent) {
3559
4751
  if (commandInput.verbose) {
3560
4752
  printVerboseFileDetails(result, commandInput, usage);
3561
4753
  }
3562
- clack4.log.error(buildFailureMessage(path9.basename(result.file), result, commandInput, usage, index === 0));
4754
+ clack4.log.error(buildFailureMessage(path11.basename(result.file), result, commandInput, usage, index === 0));
3563
4755
  continue;
3564
4756
  }
3565
4757
  const storeResult = result.storeResult ?? emptyStoreResult2();
@@ -3569,7 +4761,7 @@ function registerIngestEntriesCommand(parent) {
3569
4761
  if (commandInput.verbose) {
3570
4762
  printVerboseFileDetails(result, commandInput, usage);
3571
4763
  }
3572
- clack4.log.step(buildSuccessMessage(path9.basename(result.file), result, commandInput, usage, index === 0));
4764
+ clack4.log.step(buildSuccessMessage(path11.basename(result.file), result, commandInput, usage, index === 0));
3573
4765
  }
3574
4766
  const summaryParts = [`${totals.stored} ${pluralize3(totals.stored, "entry", "entries")} stored`, `${totals.deduped} deduped`];
3575
4767
  if (totals.rejected > 0) {
@@ -3999,7 +5191,7 @@ import { getModels } from "@earendil-works/pi-ai";
3999
5191
 
4000
5192
  // src/cli/ui.ts
4001
5193
  import os from "os";
4002
- import path10 from "path";
5194
+ import path12 from "path";
4003
5195
  import * as clack5 from "@clack/prompts";
4004
5196
  function createCliPrompts() {
4005
5197
  return {
@@ -4038,12 +5230,12 @@ function resolveUserPath(value) {
4038
5230
  return os.homedir();
4039
5231
  }
4040
5232
  if (trimmed.startsWith("~/")) {
4041
- return path10.join(os.homedir(), trimmed.slice(2));
5233
+ return path12.join(os.homedir(), trimmed.slice(2));
4042
5234
  }
4043
5235
  if (trimmed.startsWith("~\\")) {
4044
- return path10.join(os.homedir(), trimmed.slice(2));
5236
+ return path12.join(os.homedir(), trimmed.slice(2));
4045
5237
  }
4046
- return path10.resolve(trimmed);
5238
+ return path12.resolve(trimmed);
4047
5239
  }
4048
5240
  function formatPathForDisplay(filePath) {
4049
5241
  const home = os.homedir();
@@ -4375,7 +5567,8 @@ function buildNextConfig(existingConfig, values) {
4375
5567
  provider: values.provider,
4376
5568
  model: values.model,
4377
5569
  ...nextCredentials ? { credentials: nextCredentials } : { credentials: void 0 },
4378
- dbPath: values.dbPath
5570
+ dbPath: values.dbPath,
5571
+ ...values.populateAllFeatures ? { features: createAllEnabledFeatureFlagConfig() } : {}
4379
5572
  };
4380
5573
  return applySetupStageOverrides(baseConfig, values.stageOverrides);
4381
5574
  }
@@ -4566,7 +5759,8 @@ async function runSetupCore(options = {}) {
4566
5759
  primaryCredential: primaryCredential.shouldPersist ? primaryCredential.apiKey : void 0,
4567
5760
  embeddingApiKey,
4568
5761
  stageOverrides,
4569
- dbPath
5762
+ dbPath,
5763
+ populateAllFeatures: options.populateAllFeatures
4570
5764
  });
4571
5765
  const readiness = runtime.getSetupReadiness(nextConfig);
4572
5766
  const ready = readiness.ready;
@@ -5116,9 +6310,9 @@ function formatTokenCount(tokens) {
5116
6310
 
5117
6311
  // src/cli/commands/init/external-commands.ts
5118
6312
  import { execFile, execFileSync } from "child_process";
5119
- import fs6 from "fs/promises";
5120
- import path11 from "path";
5121
- var OPENCLAW_PLUGIN_PACKAGE = "@agenr/agenr-plugin";
6313
+ import fs9 from "fs/promises";
6314
+ import path13 from "path";
6315
+ var OPENCLAW_PLUGIN_PACKAGE = "@agenr/openclaw-plugin";
5122
6316
  function execAsync(command, args, options) {
5123
6317
  return new Promise((resolve, reject) => {
5124
6318
  const execOptions = {
@@ -5142,7 +6336,7 @@ function shouldUseShellForCommand(command, platform = process.platform) {
5142
6336
  if (platform !== "win32") {
5143
6337
  return false;
5144
6338
  }
5145
- const extension = path11.extname(command).toLowerCase();
6339
+ const extension = path13.extname(command).toLowerCase();
5146
6340
  return extension === ".cmd" || extension === ".bat";
5147
6341
  }
5148
6342
  function findBinaryPath(name) {
@@ -5225,12 +6419,12 @@ async function restartOpenClawGateway() {
5225
6419
  }
5226
6420
  }
5227
6421
  async function writeOpenClawPluginConfig(stateDir, config) {
5228
- const openclawConfigPath = path11.join(stateDir, "openclaw.json");
6422
+ const openclawConfigPath = path13.join(stateDir, "openclaw.json");
5229
6423
  let root = {};
5230
6424
  try {
5231
- const raw = await fs6.readFile(openclawConfigPath, "utf8");
6425
+ const raw = await fs9.readFile(openclawConfigPath, "utf8");
5232
6426
  const parsed = JSON.parse(raw);
5233
- if (isRecord(parsed)) {
6427
+ if (isRecord2(parsed)) {
5234
6428
  root = parsed;
5235
6429
  }
5236
6430
  } catch {
@@ -5254,20 +6448,20 @@ async function writeOpenClawPluginConfig(stateDir, config) {
5254
6448
  } else {
5255
6449
  delete entryConfig.configPath;
5256
6450
  }
5257
- await fs6.mkdir(stateDir, { recursive: true });
5258
- await fs6.writeFile(openclawConfigPath, `${JSON.stringify(root, null, 2)}
6451
+ await fs9.mkdir(stateDir, { recursive: true });
6452
+ await fs9.writeFile(openclawConfigPath, `${JSON.stringify(root, null, 2)}
5259
6453
  `, "utf8");
5260
6454
  return openclawConfigPath;
5261
6455
  }
5262
6456
  function shouldPersistPluginConfigPath(dbPath, configPath) {
5263
- return path11.dirname(dbPath) !== path11.dirname(configPath);
6457
+ return path13.dirname(dbPath) !== path13.dirname(configPath);
5264
6458
  }
5265
- function isRecord(value) {
6459
+ function isRecord2(value) {
5266
6460
  return typeof value === "object" && value !== null && !Array.isArray(value);
5267
6461
  }
5268
6462
  function ensureRecord(target, key) {
5269
6463
  const value = target[key];
5270
- if (isRecord(value)) {
6464
+ if (isRecord2(value)) {
5271
6465
  return value;
5272
6466
  }
5273
6467
  const created = {};
@@ -5287,21 +6481,21 @@ function ensureArrayOfStrings(target, key) {
5287
6481
  }
5288
6482
 
5289
6483
  // src/cli/commands/init/openclaw-detect.ts
5290
- import fs7 from "fs";
6484
+ import fs10 from "fs";
5291
6485
  import os2 from "os";
5292
- import path12 from "path";
6486
+ import path14 from "path";
5293
6487
  function resolveDefaultOpenClawStateDir() {
5294
- return path12.join(os2.homedir(), ".openclaw");
6488
+ return path14.join(os2.homedir(), ".openclaw");
5295
6489
  }
5296
- function detectOpenClawInstallation(env = process.env, existsSyncFn = fs7.existsSync) {
6490
+ function detectOpenClawInstallation(env = process.env, existsSyncFn = fs10.existsSync) {
5297
6491
  const envStateDir = normalizeOptionalString4(env.OPENCLAW_STATE_DIR) ?? normalizeOptionalString4(env.OPENCLAW_HOME);
5298
6492
  const source = envStateDir ? "environment" : "default";
5299
6493
  const stateDir = envStateDir ? resolveUserPath(envStateDir) : resolveDefaultOpenClawStateDir();
5300
6494
  return {
5301
6495
  detected: envStateDir !== void 0 || existsSyncFn(stateDir),
5302
6496
  stateDir,
5303
- configPath: path12.join(stateDir, "openclaw.json"),
5304
- sessionsRoot: path12.join(stateDir, "agents"),
6497
+ configPath: path14.join(stateDir, "openclaw.json"),
6498
+ sessionsRoot: path14.join(stateDir, "agents"),
5305
6499
  source
5306
6500
  };
5307
6501
  }
@@ -5311,8 +6505,8 @@ function normalizeOptionalString4(value) {
5311
6505
  }
5312
6506
 
5313
6507
  // src/cli/commands/init/session-scanner.ts
5314
- import fs8 from "fs/promises";
5315
- import path13 from "path";
6508
+ import fs11 from "fs/promises";
6509
+ import path15 from "path";
5316
6510
  async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5317
6511
  const result = {
5318
6512
  totalFiles: 0,
@@ -5335,9 +6529,9 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5335
6529
  if (!isSessionTranscriptPath(filePath)) {
5336
6530
  continue;
5337
6531
  }
5338
- let stat;
6532
+ let stat2;
5339
6533
  try {
5340
- stat = await fs8.stat(filePath);
6534
+ stat2 = await fs11.stat(filePath);
5341
6535
  } catch (error) {
5342
6536
  if (isMissingPathError(error)) {
5343
6537
  continue;
@@ -5346,16 +6540,16 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5346
6540
  }
5347
6541
  result.allFiles.push(filePath);
5348
6542
  result.totalFiles += 1;
5349
- result.totalSizeBytes += stat.size;
5350
- if (stat.mtimeMs >= cutoffMs) {
6543
+ result.totalSizeBytes += stat2.size;
6544
+ if (stat2.mtimeMs >= cutoffMs) {
5351
6545
  result.recentFiles.push(filePath);
5352
- result.recentSizeBytes += stat.size;
6546
+ result.recentSizeBytes += stat2.size;
5353
6547
  }
5354
6548
  }
5355
6549
  return result;
5356
6550
  }
5357
6551
  function isSessionTranscriptPath(filePath) {
5358
- return filePath.split(path13.sep).includes("sessions");
6552
+ return filePath.split(path15.sep).includes("sessions");
5359
6553
  }
5360
6554
  function isMissingPathError(error) {
5361
6555
  return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
@@ -5394,7 +6588,8 @@ async function runInitWizard(options = {}) {
5394
6588
  }
5395
6589
  setupResult = await runtime.runSetupCore({
5396
6590
  existingConfig: activeConfig,
5397
- prompts
6591
+ prompts,
6592
+ populateAllFeatures: true
5398
6593
  });
5399
6594
  if (!setupResult) {
5400
6595
  prompts.cancel("Init cancelled.");
@@ -5413,7 +6608,8 @@ async function runInitWizard(options = {}) {
5413
6608
  if (reconfigure) {
5414
6609
  setupResult = await runtime.runSetupCore({
5415
6610
  existingConfig: activeConfig,
5416
- prompts
6611
+ prompts,
6612
+ populateAllFeatures: true
5417
6613
  });
5418
6614
  if (!setupResult) {
5419
6615
  prompts.cancel("Init cancelled.");
@@ -5423,7 +6619,8 @@ async function runInitWizard(options = {}) {
5423
6619
  }
5424
6620
  } else {
5425
6621
  setupResult = await runtime.runSetupCore({
5426
- prompts
6622
+ prompts,
6623
+ populateAllFeatures: true
5427
6624
  });
5428
6625
  if (!setupResult) {
5429
6626
  prompts.cancel("Init cancelled.");
@@ -5679,7 +6876,7 @@ function buildNextSteps(detection, pluginStatus, gatewayStatus, sessionScan) {
5679
6876
  steps.push("Install OpenClaw later, then rerun `agenr init` to enable the plugin.");
5680
6877
  } else {
5681
6878
  if (!pluginStatus.toLowerCase().includes("installed")) {
5682
- steps.push("Install the plugin later with `openclaw plugins install @agenr/agenr-plugin`.");
6879
+ steps.push("Install the plugin later with `openclaw plugins install @agenr/openclaw-plugin`.");
5683
6880
  }
5684
6881
  if (gatewayStatus.toLowerCase().includes("manual restart")) {
5685
6882
  steps.push("Run `openclaw gateway restart` after the plugin is installed.");
@@ -5902,7 +7099,7 @@ import { InvalidArgumentError as InvalidArgumentError5, Option as Option4 } from
5902
7099
  // src/app/scenarios/claim-keys/runtime.ts
5903
7100
  import { randomUUID as randomUUID11 } from "crypto";
5904
7101
  import { mkdir as mkdir2, writeFile } from "fs/promises";
5905
- import path20 from "path";
7102
+ import path22 from "path";
5906
7103
  import { getModel } from "@earendil-works/pi-ai";
5907
7104
 
5908
7105
  // src/adapters/db/surgeon-run-log.ts
@@ -7389,8 +8586,8 @@ function createSurgeonPort(executor) {
7389
8586
 
7390
8587
  // src/app/surgeon/service.ts
7391
8588
  import { randomUUID as randomUUID9 } from "crypto";
7392
- import fs10 from "fs";
7393
- import path15 from "path";
8589
+ import fs13 from "fs";
8590
+ import path17 from "path";
7394
8591
  import { runAgentLoop } from "@earendil-works/pi-agent-core";
7395
8592
 
7396
8593
  // src/core/surgeon/domain/run-presets.ts
@@ -10116,8 +11313,8 @@ async function applyProposalToEntries(input, deps) {
10116
11313
  }
10117
11314
 
10118
11315
  // src/app/surgeon/trace-logger.ts
10119
- import fs9 from "fs";
10120
- import path14 from "path";
11316
+ import fs12 from "fs";
11317
+ import path16 from "path";
10121
11318
  var TRACE_MAX_STRING_LENGTH = 320;
10122
11319
  var TRACE_MAX_ARRAY_ITEMS = 12;
10123
11320
  var TRACE_MAX_OBJECT_KEYS = 20;
@@ -10325,8 +11522,8 @@ function appendTrace(tracePath, payload, logger) {
10325
11522
  return;
10326
11523
  }
10327
11524
  try {
10328
- fs9.mkdirSync(path14.dirname(tracePath), { recursive: true });
10329
- fs9.appendFileSync(tracePath, `${JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...payload })}
11525
+ fs12.mkdirSync(path16.dirname(tracePath), { recursive: true });
11526
+ fs12.appendFileSync(tracePath, `${JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...payload })}
10330
11527
  `, "utf8");
10331
11528
  } catch (error) {
10332
11529
  logger.warn(`failed to write trace file ${tracePath}: ${formatError2(error)}`);
@@ -12875,8 +14072,8 @@ function resolveTracePath(tracePath, passType, runId) {
12875
14072
  return void 0;
12876
14073
  }
12877
14074
  try {
12878
- if (fs10.statSync(tracePath).isDirectory()) {
12879
- return path15.join(tracePath, `surgeon-${passType}-${runId}.jsonl`);
14075
+ if (fs13.statSync(tracePath).isDirectory()) {
14076
+ return path17.join(tracePath, `surgeon-${passType}-${runId}.jsonl`);
12880
14077
  }
12881
14078
  } catch {
12882
14079
  return tracePath;
@@ -13525,7 +14722,7 @@ function toolNameToActionType(toolName) {
13525
14722
  return void 0;
13526
14723
  }
13527
14724
  function shouldAuditAction(actionType, details) {
13528
- const record = asRecord(details);
14725
+ const record = asRecord2(details);
13529
14726
  if (record.success !== true) {
13530
14727
  return false;
13531
14728
  }
@@ -13535,7 +14732,7 @@ function shouldAuditAction(actionType, details) {
13535
14732
  return record.updated === true;
13536
14733
  }
13537
14734
  function extractEntryIds(args) {
13538
- const entryId = asRecord(args).entry_id;
14735
+ const entryId = asRecord2(args).entry_id;
13539
14736
  return typeof entryId === "string" && entryId.trim().length > 0 ? [entryId.trim()] : [];
13540
14737
  }
13541
14738
  function extractActionReasoning(assistantMessage, args, details, toolName) {
@@ -13543,8 +14740,8 @@ function extractActionReasoning(assistantMessage, args, details, toolName) {
13543
14740
  if (assistantReasoning.length > 0) {
13544
14741
  return assistantReasoning;
13545
14742
  }
13546
- const argRecord = asRecord(args);
13547
- const detailRecord = asRecord(details);
14743
+ const argRecord = asRecord2(args);
14744
+ const detailRecord = asRecord2(details);
13548
14745
  const argReason = typeof argRecord.reasoning === "string" ? argRecord.reasoning.trim() : typeof argRecord.reason === "string" ? argRecord.reason.trim() : "";
13549
14746
  if (argReason.length > 0) {
13550
14747
  return argReason;
@@ -13567,7 +14764,7 @@ function formatLastRun(run) {
13567
14764
  function isAssistantMessage2(message) {
13568
14765
  return typeof message === "object" && message !== null && "role" in message && message.role === "assistant";
13569
14766
  }
13570
- function asRecord(value) {
14767
+ function asRecord2(value) {
13571
14768
  if (!value || typeof value !== "object" || Array.isArray(value)) {
13572
14769
  return {};
13573
14770
  }
@@ -13778,7 +14975,7 @@ function formatValue(value) {
13778
14975
  }
13779
14976
 
13780
14977
  // src/app/scenarios/claim-keys/deterministic-fixtures.ts
13781
- import { createHash as createHash2 } from "crypto";
14978
+ import { createHash as createHash3 } from "crypto";
13782
14979
  var DEFAULT_CONTEXT_WINDOW_TOKENS2 = 16e3;
13783
14980
  var DEFAULT_MAX_OUTPUT_TOKENS = 4e3;
13784
14981
  var EMBEDDING_DIMENSIONS = 1024;
@@ -13836,7 +15033,7 @@ function hashToVector(text2, dimensions) {
13836
15033
  const vector = [];
13837
15034
  let counter = 0;
13838
15035
  while (vector.length < dimensions) {
13839
- const block = createHash2("sha256").update(text2).update(String(counter)).digest();
15036
+ const block = createHash3("sha256").update(text2).update(String(counter)).digest();
13840
15037
  for (let offset = 0; offset + 4 <= block.length && vector.length < dimensions; offset += 4) {
13841
15038
  vector.push(block.readInt32LE(offset) / 2147483647);
13842
15039
  }
@@ -13877,8 +15074,8 @@ function isFixtureError(value) {
13877
15074
  }
13878
15075
 
13879
15076
  // src/app/scenarios/claim-keys/fixture-loader.ts
13880
- import { readFile } from "fs/promises";
13881
- import path17 from "path";
15077
+ import { readFile as readFile3 } from "fs/promises";
15078
+ import path19 from "path";
13882
15079
 
13883
15080
  // src/app/scenarios/claim-keys/validation/shared.ts
13884
15081
  var SUPPORTED_PROPOSAL_SCOPES = ["single_entry", "cluster"];
@@ -14208,12 +15405,12 @@ function readRequiredTrue(value, label, filePath) {
14208
15405
 
14209
15406
  // src/app/scenarios/claim-keys/validation/scenario-root.ts
14210
15407
  import { existsSync } from "fs";
14211
- import path16 from "path";
15408
+ import path18 from "path";
14212
15409
  import { fileURLToPath } from "url";
14213
15410
  var SCENARIO_ROOT_SEGMENTS = ["tests", "scenarios", "claim-keys"];
14214
15411
  function getDefaultClaimKeyScenarioRoot(options = {}) {
14215
- const moduleDirectory = path16.dirname(fileURLToPath(options.moduleUrl ?? import.meta.url));
14216
- const startDirectories = Array.from(/* @__PURE__ */ new Set([path16.resolve(options.cwd ?? process.cwd()), moduleDirectory]));
15412
+ const moduleDirectory = path18.dirname(fileURLToPath(options.moduleUrl ?? import.meta.url));
15413
+ const startDirectories = Array.from(/* @__PURE__ */ new Set([path18.resolve(options.cwd ?? process.cwd()), moduleDirectory]));
14217
15414
  for (const startDirectory of startDirectories) {
14218
15415
  const discovered = findScenarioRootFrom(startDirectory);
14219
15416
  if (discovered) {
@@ -14235,24 +15432,24 @@ function readOptionalRelativeFixturePath(value, label, filePath, rootDir) {
14235
15432
  return readRelativeFixturePath(value, label, filePath, rootDir);
14236
15433
  }
14237
15434
  function normalizeFixturePath(relativePath, rootDir, filePath, label) {
14238
- if (path16.isAbsolute(relativePath)) {
15435
+ if (path18.isAbsolute(relativePath)) {
14239
15436
  throw new Error(`Invalid scenario ${filePath}: ${label} must be relative to the scenario root.`);
14240
15437
  }
14241
- const resolved = path16.resolve(rootDir, relativePath);
14242
- const relative = path16.relative(rootDir, resolved);
14243
- if (relative.startsWith("..") || path16.isAbsolute(relative)) {
15438
+ const resolved = path18.resolve(rootDir, relativePath);
15439
+ const relative = path18.relative(rootDir, resolved);
15440
+ if (relative.startsWith("..") || path18.isAbsolute(relative)) {
14244
15441
  throw new Error(`Invalid scenario ${filePath}: ${label} must stay inside the scenario root.`);
14245
15442
  }
14246
- return relative.split(path16.sep).join("/");
15443
+ return relative.split(path18.sep).join("/");
14247
15444
  }
14248
15445
  function findScenarioRootFrom(startDirectory) {
14249
- let currentDirectory = path16.resolve(startDirectory);
15446
+ let currentDirectory = path18.resolve(startDirectory);
14250
15447
  while (true) {
14251
- const candidate = path16.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
15448
+ const candidate = path18.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
14252
15449
  if (existsSync(candidate)) {
14253
15450
  return candidate;
14254
15451
  }
14255
- const parentDirectory = path16.dirname(currentDirectory);
15452
+ const parentDirectory = path18.dirname(currentDirectory);
14256
15453
  if (parentDirectory === currentDirectory) {
14257
15454
  return void 0;
14258
15455
  }
@@ -14595,7 +15792,7 @@ async function loadSeedFixtureEntries(rootDir, relativePath) {
14595
15792
  if (!relativePath) {
14596
15793
  return null;
14597
15794
  }
14598
- const parsed = await readJsonFile(path17.join(rootDir, relativePath));
15795
+ const parsed = await readJsonFile(path19.join(rootDir, relativePath));
14599
15796
  const seedEntries = readSeedEntries(parsed, relativePath);
14600
15797
  if (!seedEntries) {
14601
15798
  throw new Error(`Seed fixture ${relativePath} must contain an array.`);
@@ -14658,7 +15855,7 @@ async function readArrayFixture(rootDir, relativePath) {
14658
15855
  if (!relativePath) {
14659
15856
  return null;
14660
15857
  }
14661
- const parsed = await readJsonFile(path17.join(rootDir, relativePath));
15858
+ const parsed = await readJsonFile(path19.join(rootDir, relativePath));
14662
15859
  if (!Array.isArray(parsed)) {
14663
15860
  throw new Error(`Fixture file ${relativePath} must contain a JSON array.`);
14664
15861
  }
@@ -14666,7 +15863,7 @@ async function readArrayFixture(rootDir, relativePath) {
14666
15863
  }
14667
15864
  async function readJsonFile(filePath) {
14668
15865
  try {
14669
- return JSON.parse(await readFile(filePath, "utf8"));
15866
+ return JSON.parse(await readFile3(filePath, "utf8"));
14670
15867
  } catch (error) {
14671
15868
  const message = error instanceof Error ? error.message : String(error);
14672
15869
  throw new Error(`Invalid fixture file ${filePath}: JSON parse failed - ${message}`, {
@@ -14711,8 +15908,8 @@ function readExtractionImportance(value, label, filePath) {
14711
15908
  }
14712
15909
 
14713
15910
  // src/app/scenarios/claim-keys/load-scenarios.ts
14714
- import { readdir, readFile as readFile2 } from "fs/promises";
14715
- import path18 from "path";
15911
+ import { readdir, readFile as readFile4 } from "fs/promises";
15912
+ import path20 from "path";
14716
15913
 
14717
15914
  // src/app/scenarios/claim-keys/validation/expectations.ts
14718
15915
  var EXPECTATION_KEYS = /* @__PURE__ */ new Set(["warnings", "rows", "rowCount", "proposals", "storeResult", "surgeonSummary"]);
@@ -14974,20 +16171,20 @@ function validateClaimKeyScenario(input, filePath, rootDir = getDefaultClaimKeyS
14974
16171
  async function discoverScenarioFiles(rootDir) {
14975
16172
  const files = [];
14976
16173
  for (const kind of SUPPORTED_KINDS2) {
14977
- const directory = path18.join(rootDir, kind);
16174
+ const directory = path20.join(rootDir, kind);
14978
16175
  const entries = await readdir(directory, { withFileTypes: true });
14979
16176
  for (const entry of entries) {
14980
16177
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
14981
16178
  continue;
14982
16179
  }
14983
- files.push(path18.join(directory, entry.name));
16180
+ files.push(path20.join(directory, entry.name));
14984
16181
  }
14985
16182
  }
14986
16183
  return files.sort();
14987
16184
  }
14988
16185
  async function readScenarioJsonFile(filePath) {
14989
16186
  try {
14990
- return JSON.parse(await readFile2(filePath, "utf8"));
16187
+ return JSON.parse(await readFile4(filePath, "utf8"));
14991
16188
  } catch (error) {
14992
16189
  const message = error instanceof Error ? error.message : String(error);
14993
16190
  throw new Error(`Invalid scenario ${filePath}: JSON parse failed - ${message}`, {
@@ -14998,11 +16195,11 @@ async function readScenarioJsonFile(filePath) {
14998
16195
 
14999
16196
  // src/app/scenarios/claim-keys/sandbox.ts
15000
16197
  import { mkdir, rm } from "fs/promises";
15001
- import path19 from "path";
16198
+ import path21 from "path";
15002
16199
  var SANDBOX_DB_FILENAME = "knowledge.db";
15003
16200
  async function createClaimKeyScenarioSandbox(root) {
15004
- const resolvedRoot = path19.resolve(root);
15005
- const dbPath = path19.join(resolvedRoot, SANDBOX_DB_FILENAME);
16201
+ const resolvedRoot = path21.resolve(root);
16202
+ const dbPath = path21.join(resolvedRoot, SANDBOX_DB_FILENAME);
15006
16203
  await mkdir(resolvedRoot, { recursive: true });
15007
16204
  await removeDatabaseFiles(dbPath);
15008
16205
  const database = await createDatabase(dbPath);
@@ -15193,7 +16390,7 @@ async function listClaimKeyScenariosRuntime(options = {}) {
15193
16390
  }
15194
16391
  async function runClaimKeyScenariosRuntime(options = {}) {
15195
16392
  const runId = buildRunId();
15196
- const artifactRoot = path20.resolve(DEFAULT_ARTIFACT_ROOT, runId);
16393
+ const artifactRoot = path22.resolve(DEFAULT_ARTIFACT_ROOT, runId);
15197
16394
  await mkdir2(artifactRoot, { recursive: true });
15198
16395
  let scenarios;
15199
16396
  try {
@@ -15245,11 +16442,11 @@ function filterClaimKeyScenarios(scenarios, options) {
15245
16442
  }
15246
16443
  async function runOneClaimKeyScenario(scenario, options) {
15247
16444
  const startedAt = Date.now();
15248
- const scenarioArtifactRoot = path20.join(options.artifactRoot, scenario.id);
15249
- const sandboxRoot = path20.join(scenarioArtifactRoot, "sandbox");
16445
+ const scenarioArtifactRoot = path22.join(options.artifactRoot, scenario.id);
16446
+ const sandboxRoot = path22.join(scenarioArtifactRoot, "sandbox");
15250
16447
  const warnings = [];
15251
16448
  await mkdir2(scenarioArtifactRoot, { recursive: true });
15252
- await writeJson(path20.join(scenarioArtifactRoot, "scenario.json"), scenario);
16449
+ await writeJson(path22.join(scenarioArtifactRoot, "scenario.json"), scenario);
15253
16450
  let actual = {
15254
16451
  warnings: [],
15255
16452
  rows: [],
@@ -15362,7 +16559,7 @@ async function runIngestScenario(scenario, database, warnings, rootDir) {
15362
16559
  throw new Error(`Scenario ${scenario.id} is missing extraction fixture responses.`);
15363
16560
  }
15364
16561
  const result = await ingestPath(
15365
- path20.join(rootDir, scenario.input.transcriptFile),
16562
+ path22.join(rootDir, scenario.input.transcriptFile),
15366
16563
  {
15367
16564
  files: localTranscriptFiles,
15368
16565
  transcript: openClawTranscriptParser,
@@ -15504,20 +16701,20 @@ async function loadLatestSurgeonProposals(database, runId) {
15504
16701
  }));
15505
16702
  }
15506
16703
  async function writeScenarioArtifacts(scenarioArtifactRoot, actual, assertionResults, diffSummary) {
15507
- await writeJson(path20.join(scenarioArtifactRoot, "actual.json"), actual);
15508
- await writeJson(path20.join(scenarioArtifactRoot, "diff.json"), {
16704
+ await writeJson(path22.join(scenarioArtifactRoot, "actual.json"), actual);
16705
+ await writeJson(path22.join(scenarioArtifactRoot, "diff.json"), {
15509
16706
  assertions: assertionResults,
15510
16707
  diffSummary
15511
16708
  });
15512
- await writeJson(path20.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
16709
+ await writeJson(path22.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
15513
16710
  if (actual.storeResult) {
15514
- await writeJson(path20.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
16711
+ await writeJson(path22.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
15515
16712
  }
15516
16713
  if (actual.surgeonSummary) {
15517
- await writeJson(path20.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
16714
+ await writeJson(path22.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
15518
16715
  }
15519
16716
  if (actual.proposals.length > 0) {
15520
- await writeJson(path20.join(scenarioArtifactRoot, "proposals.json"), actual.proposals);
16717
+ await writeJson(path22.join(scenarioArtifactRoot, "proposals.json"), actual.proposals);
15521
16718
  }
15522
16719
  }
15523
16720
  async function writeJson(filePath, value) {
@@ -15649,7 +16846,7 @@ import { InvalidArgumentError as InvalidArgumentError6, Option as Option5 } from
15649
16846
 
15650
16847
  // src/app/surgeon/runtime.ts
15651
16848
  import { copyFile, mkdir as mkdir3 } from "fs/promises";
15652
- import path21 from "path";
16849
+ import path23 from "path";
15653
16850
  import { fileURLToPath as fileURLToPath2 } from "url";
15654
16851
  import { getModel as getModel2 } from "@earendil-works/pi-ai";
15655
16852
  var DEFAULT_SURGEON_PROVIDER = "openai";
@@ -15894,7 +17091,7 @@ async function backupDatabaseFile(dbPath) {
15894
17091
  }
15895
17092
  const sourcePath = resolveFilesystemPath(dbPath);
15896
17093
  const backupPath = `${sourcePath}.surgeon-backup-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-")}`;
15897
- await mkdir3(path21.dirname(backupPath), { recursive: true });
17094
+ await mkdir3(path23.dirname(backupPath), { recursive: true });
15898
17095
  await copyFile(sourcePath, backupPath);
15899
17096
  await copySidecarIfPresent(`${sourcePath}-wal`, `${backupPath}-wal`);
15900
17097
  await copySidecarIfPresent(`${sourcePath}-shm`, `${backupPath}-shm`);
@@ -15912,12 +17109,12 @@ async function copySidecarIfPresent(sourcePath, targetPath) {
15912
17109
  }
15913
17110
  function resolveFilesystemPath(value) {
15914
17111
  if (!value.startsWith("file:")) {
15915
- return path21.resolve(value);
17112
+ return path23.resolve(value);
15916
17113
  }
15917
17114
  try {
15918
17115
  return fileURLToPath2(value);
15919
17116
  } catch {
15920
- return path21.resolve(value.slice("file:".length));
17117
+ return path23.resolve(value.slice("file:".length));
15921
17118
  }
15922
17119
  }
15923
17120
  function isMissingFileError2(error) {