agenr 3.1.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-JSVQILB3.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-EEEL53X4.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,
@@ -93,6 +89,7 @@ import {
93
89
  resolveEmbeddingModel,
94
90
  resolveLlmApiKey,
95
91
  resolveLlmCredentials,
92
+ resolveLocalFilesystemPath,
96
93
  resolveModel,
97
94
  retireEntry,
98
95
  supersedeEntry,
@@ -100,7 +97,7 @@ import {
100
97
  updateEntry,
101
98
  validateTemporalValidityRange,
102
99
  writeConfig
103
- } from "./chunk-NNO2V4GH.js";
100
+ } from "./chunk-Z5X7T4QZ.js";
104
101
  import {
105
102
  compactClaimKey,
106
103
  describeClaimKeyNormalizationFailure,
@@ -168,7 +165,6 @@ function formatLabel(label, value) {
168
165
  // src/cli/commands/db.ts
169
166
  import fs from "fs/promises";
170
167
  import path from "path";
171
- import { fileURLToPath } from "url";
172
168
  import * as clack from "@clack/prompts";
173
169
  function registerDbCommand(program2) {
174
170
  const dbCommand = program2.command("db").description("Database utilities");
@@ -199,25 +195,11 @@ function registerDbCommand(program2) {
199
195
  });
200
196
  }
201
197
  function resolveResetPath(dbPath) {
202
- if (dbPath === ":memory:") {
198
+ const trimmedPath = dbPath.trim();
199
+ if (trimmedPath === ":memory:") {
203
200
  return { displayPath: dbPath };
204
201
  }
205
- if (dbPath.startsWith("file:")) {
206
- try {
207
- const filePath = fileURLToPath(dbPath);
208
- return {
209
- deletePath: filePath,
210
- displayPath: filePath
211
- };
212
- } catch {
213
- const resolvedPath2 = path.resolve(dbPath.slice("file:".length));
214
- return {
215
- deletePath: resolvedPath2,
216
- displayPath: resolvedPath2
217
- };
218
- }
219
- }
220
- const resolvedPath = path.resolve(dbPath);
202
+ const resolvedPath = resolveLocalFilesystemPath(trimmedPath) ?? path.resolve(trimmedPath);
221
203
  return {
222
204
  deletePath: resolvedPath,
223
205
  displayPath: resolvedPath
@@ -244,7 +226,7 @@ function formatUnknownError(error) {
244
226
  }
245
227
 
246
228
  // src/cli/commands/ingest.ts
247
- import path9 from "path";
229
+ import path11 from "path";
248
230
  import * as clack4 from "@clack/prompts";
249
231
 
250
232
  // src/core/ingestion/parser.ts
@@ -2225,8 +2207,8 @@ import path3 from "path";
2225
2207
  var GENERIC_TRANSCRIPT_FILE_PATTERN = /^.+\.jsonl(?:\.(?:reset|deleted)\..+)?$/iu;
2226
2208
  async function discoverTranscriptFiles(targetPath, options = {}) {
2227
2209
  const resolvedTargetPath = path3.resolve(targetPath);
2228
- const stat = await fs2.stat(resolvedTargetPath);
2229
- if (stat.isFile()) {
2210
+ const stat2 = await fs2.stat(resolvedTargetPath);
2211
+ if (stat2.isFile()) {
2230
2212
  return matchesTranscriptFileName(path3.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
2231
2213
  }
2232
2214
  const recursive = options.recursive ?? true;
@@ -2245,6 +2227,1037 @@ function matchesTranscriptFileName(fileName) {
2245
2227
  return GENERIC_TRANSCRIPT_FILE_PATTERN.test(fileName.trim());
2246
2228
  }
2247
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
+
2248
3261
  // src/cli/shared/parse.ts
2249
3262
  import { InvalidArgumentError } from "commander";
2250
3263
  function normalizeOptionalString2(value) {
@@ -2379,13 +3392,13 @@ function timestamp() {
2379
3392
  import { InvalidArgumentError as InvalidArgumentError3, Option as Option2 } from "commander";
2380
3393
 
2381
3394
  // src/cli/commands/ingest-episodes.ts
2382
- import fs4 from "fs/promises";
2383
- import path6 from "path";
3395
+ import fs7 from "fs/promises";
3396
+ import path8 from "path";
2384
3397
  import * as clack2 from "@clack/prompts";
2385
3398
  import { InvalidArgumentError as InvalidArgumentError2, Option } from "commander";
2386
3399
 
2387
3400
  // src/adapters/db/episode-ingest-support.ts
2388
- import path4 from "path";
3401
+ import path5 from "path";
2389
3402
  function createEpisodeIngestSupportPort(executor) {
2390
3403
  return {
2391
3404
  countEntries: async () => countRows(executor, "SELECT COUNT(*) AS count FROM entries"),
@@ -2401,7 +3414,7 @@ async function hasRelevantProvenanceMatch(executor, sampleFiles) {
2401
3414
  if (ingestLogMatches > 0) {
2402
3415
  return true;
2403
3416
  }
2404
- 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))));
2405
3418
  const basenameClauses = basenames.map(() => "(source_file = ? OR source_file LIKE ?)").join(" OR ");
2406
3419
  const basenameArgs = basenames.flatMap((basename) => [basename, `%/${basename}`]);
2407
3420
  const entryMatches = await countRows(executor, `SELECT COUNT(*) AS count FROM entries WHERE source_file IS NOT NULL AND (${basenameClauses})`, basenameArgs);
@@ -2424,6 +3437,171 @@ async function countRows(executor, sql, args = []) {
2424
3437
  return Number.isFinite(normalized) ? normalized : 0;
2425
3438
  }
2426
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
+
2427
3605
  // src/adapters/openclaw/session/session-registry.ts
2428
3606
  var GENERIC_AGENT_SESSION_KEY_PATTERN = /^agent:([^:]+):/i;
2429
3607
  async function loadOpenClawSessionRegistry(sessionsDir) {
@@ -2490,17 +3668,17 @@ function normalizeNullableString(value) {
2490
3668
  }
2491
3669
 
2492
3670
  // src/adapters/openclaw/session/transcript-files.ts
2493
- import fs3 from "fs/promises";
2494
- import path5 from "path";
3671
+ import fs6 from "fs/promises";
3672
+ import path7 from "path";
2495
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;
2496
3674
  async function discoverOpenClawTranscriptFiles(targetPath) {
2497
- const resolvedTargetPath = path5.resolve(targetPath);
2498
- const stat = await fs3.stat(resolvedTargetPath);
2499
- if (stat.isFile()) {
2500
- 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] : [];
2501
3679
  }
2502
- const entries = await fs3.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true });
2503
- 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));
2504
3682
  }
2505
3683
  var openClawTranscriptFiles = {
2506
3684
  discoverFiles: discoverOpenClawTranscriptFiles
@@ -2547,7 +3725,7 @@ function registerIngestEpisodesCommand(parent) {
2547
3725
  }
2548
3726
  const { provider, modelId } = resolveEpisodeModel(config, commandInput.modelOverride);
2549
3727
  const modelInfo = createEpisodeIngestSummaryModelInfo(provider, modelId);
2550
- const resolvedTargetPath = path6.resolve(normalizedTargetPath);
3728
+ const resolvedTargetPath = path8.resolve(normalizedTargetPath);
2551
3729
  const sessionsDir = await resolveSessionsDirectory(resolvedTargetPath);
2552
3730
  const ports = await createEpisodeIngestPorts(database, sessionsDir);
2553
3731
  const embeddingSetup = resolveEpisodeEmbeddingSetup(config, commandInput);
@@ -2762,8 +3940,8 @@ function createEpisodeIngestSummaryLlm(provider, modelId, apiKey) {
2762
3940
  };
2763
3941
  }
2764
3942
  async function resolveSessionsDirectory(targetPath) {
2765
- const stat = await fs4.stat(targetPath);
2766
- return stat.isFile() ? path6.dirname(targetPath) : targetPath;
3943
+ const stat2 = await fs7.stat(targetPath);
3944
+ return stat2.isFile() ? path8.dirname(targetPath) : targetPath;
2767
3945
  }
2768
3946
  function resolveEpisodeDbPath(config, overridePath) {
2769
3947
  const normalizedOverride = normalizeOptionalString2(overridePath);
@@ -2773,7 +3951,7 @@ function resolveEpisodeDbPath(config, overridePath) {
2773
3951
  if (normalizedOverride === ":memory:" || normalizedOverride.startsWith("file:")) {
2774
3952
  return normalizedOverride;
2775
3953
  }
2776
- return path6.resolve(normalizedOverride);
3954
+ return path8.resolve(normalizedOverride);
2777
3955
  }
2778
3956
  function resolveEpisodeModel(config, overrideRef) {
2779
3957
  const resolved = resolveModel(config, "episode");
@@ -2887,7 +4065,7 @@ function reportEpisodeProgress(completed, total, session) {
2887
4065
  }
2888
4066
  function formatEpisodeProgressLine(completed, total, session) {
2889
4067
  const prefix = completed !== void 0 && total !== void 0 ? `${completed}/${total} ` : "";
2890
- const fileLabel = path6.basename(session.filePath);
4068
+ const fileLabel = path8.basename(session.filePath);
2891
4069
  const details = session.action === "failed" ? `failed (${session.error ?? "unknown error"})` : session.action;
2892
4070
  const usage = session.usage.totalCost > 0 ? ` ${formatCost(session.usage.totalCost)}` : "";
2893
4071
  return `${prefix}${fileLabel}: ${details}${usage}`;
@@ -2931,26 +4109,26 @@ function parseEpisodeIngestConcurrency(value) {
2931
4109
  }
2932
4110
 
2933
4111
  // src/cli/commands/ingest-procedures.ts
2934
- import path8 from "path";
4112
+ import path10 from "path";
2935
4113
  import * as clack3 from "@clack/prompts";
2936
4114
  import "commander";
2937
4115
 
2938
4116
  // src/adapters/files/procedure-files.ts
2939
- import fs5 from "fs/promises";
2940
- import path7 from "path";
4117
+ import fs8 from "fs/promises";
4118
+ import path9 from "path";
2941
4119
  var PROCEDURE_FILE_PATTERN = /^.+\.ya?ml$/iu;
2942
4120
  async function discoverProcedureFiles(targetPath, options = {}) {
2943
- const resolvedTargetPath = path7.resolve(targetPath);
2944
- const stat = await fs5.stat(resolvedTargetPath);
2945
- if (stat.isFile()) {
2946
- 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] : [];
2947
4125
  }
2948
4126
  const recursive = options.recursive ?? true;
2949
- const entries = recursive ? await fs5.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true }) : await fs5.readdir(resolvedTargetPath, { withFileTypes: true });
2950
- 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));
2951
4129
  }
2952
4130
  async function readProcedureFile(filePath) {
2953
- return fs5.readFile(filePath, "utf-8");
4131
+ return fs8.readFile(filePath, "utf-8");
2954
4132
  }
2955
4133
  var LocalProcedureFiles = class {
2956
4134
  /**
@@ -3285,7 +4463,7 @@ function registerIngestProceduresCommand(parent) {
3285
4463
  try {
3286
4464
  const config = readConfig();
3287
4465
  const dbPath = config.dbPath;
3288
- const resolvedTargetPath = path8.resolve(commandInput.targetPath);
4466
+ const resolvedTargetPath = path10.resolve(commandInput.targetPath);
3289
4467
  database = await createDatabase(dbPath);
3290
4468
  if (commandInput.verbose) {
3291
4469
  clack3.log.step(`Preparing procedure sync for ${resolvedTargetPath}...`);
@@ -3462,11 +4640,11 @@ function registerIngestEntriesCommand(parent) {
3462
4640
  const claimApiKey = claimModel ? resolveLlmApiKey(config, claimModel.provider) : void 0;
3463
4641
  const sharedEmbedding = createEmbeddingClient(resolveEmbeddingApiKey(config), resolveEmbeddingModel(config));
3464
4642
  if (commandInput.verbose) {
3465
- clack4.log.step(`Discovering transcript files in ${path9.resolve(commandInput.targetPath)}...`);
4643
+ clack4.log.step(`Discovering transcript files in ${path11.resolve(commandInput.targetPath)}...`);
3466
4644
  }
3467
4645
  const files = await localTranscriptFiles.discoverFiles(commandInput.targetPath);
3468
4646
  if (files.length === 0) {
3469
- 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)}.`);
3470
4648
  clack4.outro("Nothing to ingest.");
3471
4649
  return;
3472
4650
  }
@@ -3562,9 +4740,9 @@ function registerIngestEntriesCommand(parent) {
3562
4740
  totals.skippedFiles += 1;
3563
4741
  if (commandInput.verbose) {
3564
4742
  printVerboseFileDetails(result, commandInput, usage);
3565
- clack4.log.step(buildSkippedMessage(path9.basename(result.file)));
4743
+ clack4.log.step(buildSkippedMessage(path11.basename(result.file)));
3566
4744
  } else {
3567
- clack4.log.step(buildSkippedMessage(path9.basename(result.file)));
4745
+ clack4.log.step(buildSkippedMessage(path11.basename(result.file)));
3568
4746
  }
3569
4747
  continue;
3570
4748
  }
@@ -3573,7 +4751,7 @@ function registerIngestEntriesCommand(parent) {
3573
4751
  if (commandInput.verbose) {
3574
4752
  printVerboseFileDetails(result, commandInput, usage);
3575
4753
  }
3576
- 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));
3577
4755
  continue;
3578
4756
  }
3579
4757
  const storeResult = result.storeResult ?? emptyStoreResult2();
@@ -3583,7 +4761,7 @@ function registerIngestEntriesCommand(parent) {
3583
4761
  if (commandInput.verbose) {
3584
4762
  printVerboseFileDetails(result, commandInput, usage);
3585
4763
  }
3586
- 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));
3587
4765
  }
3588
4766
  const summaryParts = [`${totals.stored} ${pluralize3(totals.stored, "entry", "entries")} stored`, `${totals.deduped} deduped`];
3589
4767
  if (totals.rejected > 0) {
@@ -4013,7 +5191,7 @@ import { getModels } from "@earendil-works/pi-ai";
4013
5191
 
4014
5192
  // src/cli/ui.ts
4015
5193
  import os from "os";
4016
- import path10 from "path";
5194
+ import path12 from "path";
4017
5195
  import * as clack5 from "@clack/prompts";
4018
5196
  function createCliPrompts() {
4019
5197
  return {
@@ -4052,12 +5230,12 @@ function resolveUserPath(value) {
4052
5230
  return os.homedir();
4053
5231
  }
4054
5232
  if (trimmed.startsWith("~/")) {
4055
- return path10.join(os.homedir(), trimmed.slice(2));
5233
+ return path12.join(os.homedir(), trimmed.slice(2));
4056
5234
  }
4057
5235
  if (trimmed.startsWith("~\\")) {
4058
- return path10.join(os.homedir(), trimmed.slice(2));
5236
+ return path12.join(os.homedir(), trimmed.slice(2));
4059
5237
  }
4060
- return path10.resolve(trimmed);
5238
+ return path12.resolve(trimmed);
4061
5239
  }
4062
5240
  function formatPathForDisplay(filePath) {
4063
5241
  const home = os.homedir();
@@ -4389,7 +5567,8 @@ function buildNextConfig(existingConfig, values) {
4389
5567
  provider: values.provider,
4390
5568
  model: values.model,
4391
5569
  ...nextCredentials ? { credentials: nextCredentials } : { credentials: void 0 },
4392
- dbPath: values.dbPath
5570
+ dbPath: values.dbPath,
5571
+ ...values.populateAllFeatures ? { features: createAllEnabledFeatureFlagConfig() } : {}
4393
5572
  };
4394
5573
  return applySetupStageOverrides(baseConfig, values.stageOverrides);
4395
5574
  }
@@ -4580,7 +5759,8 @@ async function runSetupCore(options = {}) {
4580
5759
  primaryCredential: primaryCredential.shouldPersist ? primaryCredential.apiKey : void 0,
4581
5760
  embeddingApiKey,
4582
5761
  stageOverrides,
4583
- dbPath
5762
+ dbPath,
5763
+ populateAllFeatures: options.populateAllFeatures
4584
5764
  });
4585
5765
  const readiness = runtime.getSetupReadiness(nextConfig);
4586
5766
  const ready = readiness.ready;
@@ -5130,12 +6310,16 @@ function formatTokenCount(tokens) {
5130
6310
 
5131
6311
  // src/cli/commands/init/external-commands.ts
5132
6312
  import { execFile, execFileSync } from "child_process";
5133
- import fs6 from "fs/promises";
5134
- import path11 from "path";
5135
- 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";
5136
6316
  function execAsync(command, args, options) {
5137
6317
  return new Promise((resolve, reject) => {
5138
- execFile(command, args, options, (error, stdout, stderr) => {
6318
+ const execOptions = {
6319
+ ...options,
6320
+ ...shouldUseShellForCommand(command) ? { shell: true } : {}
6321
+ };
6322
+ execFile(command, args, execOptions, (error, stdout, stderr) => {
5139
6323
  if (error) {
5140
6324
  const message = [String(stderr ?? "").trim(), error.message].filter((value) => value.length > 0).join("\n");
5141
6325
  reject(new Error(message || error.message));
@@ -5148,11 +6332,18 @@ function execAsync(command, args, options) {
5148
6332
  });
5149
6333
  });
5150
6334
  }
6335
+ function shouldUseShellForCommand(command, platform = process.platform) {
6336
+ if (platform !== "win32") {
6337
+ return false;
6338
+ }
6339
+ const extension = path13.extname(command).toLowerCase();
6340
+ return extension === ".cmd" || extension === ".bat";
6341
+ }
5151
6342
  function findBinaryPath(name) {
5152
6343
  try {
5153
6344
  const lookupCommand = process.platform === "win32" ? "where" : "which";
5154
6345
  const output = execFileSync(lookupCommand, [name], { encoding: "utf8" }).trim();
5155
- const firstLine = output.split("\n")[0]?.trim();
6346
+ const firstLine = output.split(/\r?\n/u)[0]?.trim();
5156
6347
  return firstLine && firstLine.length > 0 ? firstLine : null;
5157
6348
  } catch {
5158
6349
  return null;
@@ -5228,12 +6419,12 @@ async function restartOpenClawGateway() {
5228
6419
  }
5229
6420
  }
5230
6421
  async function writeOpenClawPluginConfig(stateDir, config) {
5231
- const openclawConfigPath = path11.join(stateDir, "openclaw.json");
6422
+ const openclawConfigPath = path13.join(stateDir, "openclaw.json");
5232
6423
  let root = {};
5233
6424
  try {
5234
- const raw = await fs6.readFile(openclawConfigPath, "utf8");
6425
+ const raw = await fs9.readFile(openclawConfigPath, "utf8");
5235
6426
  const parsed = JSON.parse(raw);
5236
- if (isRecord(parsed)) {
6427
+ if (isRecord2(parsed)) {
5237
6428
  root = parsed;
5238
6429
  }
5239
6430
  } catch {
@@ -5257,20 +6448,20 @@ async function writeOpenClawPluginConfig(stateDir, config) {
5257
6448
  } else {
5258
6449
  delete entryConfig.configPath;
5259
6450
  }
5260
- await fs6.mkdir(stateDir, { recursive: true });
5261
- 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)}
5262
6453
  `, "utf8");
5263
6454
  return openclawConfigPath;
5264
6455
  }
5265
6456
  function shouldPersistPluginConfigPath(dbPath, configPath) {
5266
- return path11.dirname(dbPath) !== path11.dirname(configPath);
6457
+ return path13.dirname(dbPath) !== path13.dirname(configPath);
5267
6458
  }
5268
- function isRecord(value) {
6459
+ function isRecord2(value) {
5269
6460
  return typeof value === "object" && value !== null && !Array.isArray(value);
5270
6461
  }
5271
6462
  function ensureRecord(target, key) {
5272
6463
  const value = target[key];
5273
- if (isRecord(value)) {
6464
+ if (isRecord2(value)) {
5274
6465
  return value;
5275
6466
  }
5276
6467
  const created = {};
@@ -5290,21 +6481,21 @@ function ensureArrayOfStrings(target, key) {
5290
6481
  }
5291
6482
 
5292
6483
  // src/cli/commands/init/openclaw-detect.ts
5293
- import fs7 from "fs";
6484
+ import fs10 from "fs";
5294
6485
  import os2 from "os";
5295
- import path12 from "path";
6486
+ import path14 from "path";
5296
6487
  function resolveDefaultOpenClawStateDir() {
5297
- return path12.join(os2.homedir(), ".openclaw");
6488
+ return path14.join(os2.homedir(), ".openclaw");
5298
6489
  }
5299
- function detectOpenClawInstallation(env = process.env, existsSyncFn = fs7.existsSync) {
6490
+ function detectOpenClawInstallation(env = process.env, existsSyncFn = fs10.existsSync) {
5300
6491
  const envStateDir = normalizeOptionalString4(env.OPENCLAW_STATE_DIR) ?? normalizeOptionalString4(env.OPENCLAW_HOME);
5301
6492
  const source = envStateDir ? "environment" : "default";
5302
6493
  const stateDir = envStateDir ? resolveUserPath(envStateDir) : resolveDefaultOpenClawStateDir();
5303
6494
  return {
5304
6495
  detected: envStateDir !== void 0 || existsSyncFn(stateDir),
5305
6496
  stateDir,
5306
- configPath: path12.join(stateDir, "openclaw.json"),
5307
- sessionsRoot: path12.join(stateDir, "agents"),
6497
+ configPath: path14.join(stateDir, "openclaw.json"),
6498
+ sessionsRoot: path14.join(stateDir, "agents"),
5308
6499
  source
5309
6500
  };
5310
6501
  }
@@ -5314,8 +6505,8 @@ function normalizeOptionalString4(value) {
5314
6505
  }
5315
6506
 
5316
6507
  // src/cli/commands/init/session-scanner.ts
5317
- import fs8 from "fs/promises";
5318
- import path13 from "path";
6508
+ import fs11 from "fs/promises";
6509
+ import path15 from "path";
5319
6510
  async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5320
6511
  const result = {
5321
6512
  totalFiles: 0,
@@ -5338,9 +6529,9 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5338
6529
  if (!isSessionTranscriptPath(filePath)) {
5339
6530
  continue;
5340
6531
  }
5341
- let stat;
6532
+ let stat2;
5342
6533
  try {
5343
- stat = await fs8.stat(filePath);
6534
+ stat2 = await fs11.stat(filePath);
5344
6535
  } catch (error) {
5345
6536
  if (isMissingPathError(error)) {
5346
6537
  continue;
@@ -5349,16 +6540,16 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
5349
6540
  }
5350
6541
  result.allFiles.push(filePath);
5351
6542
  result.totalFiles += 1;
5352
- result.totalSizeBytes += stat.size;
5353
- if (stat.mtimeMs >= cutoffMs) {
6543
+ result.totalSizeBytes += stat2.size;
6544
+ if (stat2.mtimeMs >= cutoffMs) {
5354
6545
  result.recentFiles.push(filePath);
5355
- result.recentSizeBytes += stat.size;
6546
+ result.recentSizeBytes += stat2.size;
5356
6547
  }
5357
6548
  }
5358
6549
  return result;
5359
6550
  }
5360
6551
  function isSessionTranscriptPath(filePath) {
5361
- return filePath.split(path13.sep).includes("sessions");
6552
+ return filePath.split(path15.sep).includes("sessions");
5362
6553
  }
5363
6554
  function isMissingPathError(error) {
5364
6555
  return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
@@ -5397,7 +6588,8 @@ async function runInitWizard(options = {}) {
5397
6588
  }
5398
6589
  setupResult = await runtime.runSetupCore({
5399
6590
  existingConfig: activeConfig,
5400
- prompts
6591
+ prompts,
6592
+ populateAllFeatures: true
5401
6593
  });
5402
6594
  if (!setupResult) {
5403
6595
  prompts.cancel("Init cancelled.");
@@ -5416,7 +6608,8 @@ async function runInitWizard(options = {}) {
5416
6608
  if (reconfigure) {
5417
6609
  setupResult = await runtime.runSetupCore({
5418
6610
  existingConfig: activeConfig,
5419
- prompts
6611
+ prompts,
6612
+ populateAllFeatures: true
5420
6613
  });
5421
6614
  if (!setupResult) {
5422
6615
  prompts.cancel("Init cancelled.");
@@ -5426,7 +6619,8 @@ async function runInitWizard(options = {}) {
5426
6619
  }
5427
6620
  } else {
5428
6621
  setupResult = await runtime.runSetupCore({
5429
- prompts
6622
+ prompts,
6623
+ populateAllFeatures: true
5430
6624
  });
5431
6625
  if (!setupResult) {
5432
6626
  prompts.cancel("Init cancelled.");
@@ -5682,7 +6876,7 @@ function buildNextSteps(detection, pluginStatus, gatewayStatus, sessionScan) {
5682
6876
  steps.push("Install OpenClaw later, then rerun `agenr init` to enable the plugin.");
5683
6877
  } else {
5684
6878
  if (!pluginStatus.toLowerCase().includes("installed")) {
5685
- 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`.");
5686
6880
  }
5687
6881
  if (gatewayStatus.toLowerCase().includes("manual restart")) {
5688
6882
  steps.push("Run `openclaw gateway restart` after the plugin is installed.");
@@ -5905,7 +7099,7 @@ import { InvalidArgumentError as InvalidArgumentError5, Option as Option4 } from
5905
7099
  // src/app/scenarios/claim-keys/runtime.ts
5906
7100
  import { randomUUID as randomUUID11 } from "crypto";
5907
7101
  import { mkdir as mkdir2, writeFile } from "fs/promises";
5908
- import path20 from "path";
7102
+ import path22 from "path";
5909
7103
  import { getModel } from "@earendil-works/pi-ai";
5910
7104
 
5911
7105
  // src/adapters/db/surgeon-run-log.ts
@@ -7392,8 +8586,8 @@ function createSurgeonPort(executor) {
7392
8586
 
7393
8587
  // src/app/surgeon/service.ts
7394
8588
  import { randomUUID as randomUUID9 } from "crypto";
7395
- import fs10 from "fs";
7396
- import path15 from "path";
8589
+ import fs13 from "fs";
8590
+ import path17 from "path";
7397
8591
  import { runAgentLoop } from "@earendil-works/pi-agent-core";
7398
8592
 
7399
8593
  // src/core/surgeon/domain/run-presets.ts
@@ -10119,8 +11313,8 @@ async function applyProposalToEntries(input, deps) {
10119
11313
  }
10120
11314
 
10121
11315
  // src/app/surgeon/trace-logger.ts
10122
- import fs9 from "fs";
10123
- import path14 from "path";
11316
+ import fs12 from "fs";
11317
+ import path16 from "path";
10124
11318
  var TRACE_MAX_STRING_LENGTH = 320;
10125
11319
  var TRACE_MAX_ARRAY_ITEMS = 12;
10126
11320
  var TRACE_MAX_OBJECT_KEYS = 20;
@@ -10328,8 +11522,8 @@ function appendTrace(tracePath, payload, logger) {
10328
11522
  return;
10329
11523
  }
10330
11524
  try {
10331
- fs9.mkdirSync(path14.dirname(tracePath), { recursive: true });
10332
- 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 })}
10333
11527
  `, "utf8");
10334
11528
  } catch (error) {
10335
11529
  logger.warn(`failed to write trace file ${tracePath}: ${formatError2(error)}`);
@@ -12878,8 +14072,8 @@ function resolveTracePath(tracePath, passType, runId) {
12878
14072
  return void 0;
12879
14073
  }
12880
14074
  try {
12881
- if (fs10.statSync(tracePath).isDirectory()) {
12882
- return path15.join(tracePath, `surgeon-${passType}-${runId}.jsonl`);
14075
+ if (fs13.statSync(tracePath).isDirectory()) {
14076
+ return path17.join(tracePath, `surgeon-${passType}-${runId}.jsonl`);
12883
14077
  }
12884
14078
  } catch {
12885
14079
  return tracePath;
@@ -13528,7 +14722,7 @@ function toolNameToActionType(toolName) {
13528
14722
  return void 0;
13529
14723
  }
13530
14724
  function shouldAuditAction(actionType, details) {
13531
- const record = asRecord(details);
14725
+ const record = asRecord2(details);
13532
14726
  if (record.success !== true) {
13533
14727
  return false;
13534
14728
  }
@@ -13538,7 +14732,7 @@ function shouldAuditAction(actionType, details) {
13538
14732
  return record.updated === true;
13539
14733
  }
13540
14734
  function extractEntryIds(args) {
13541
- const entryId = asRecord(args).entry_id;
14735
+ const entryId = asRecord2(args).entry_id;
13542
14736
  return typeof entryId === "string" && entryId.trim().length > 0 ? [entryId.trim()] : [];
13543
14737
  }
13544
14738
  function extractActionReasoning(assistantMessage, args, details, toolName) {
@@ -13546,8 +14740,8 @@ function extractActionReasoning(assistantMessage, args, details, toolName) {
13546
14740
  if (assistantReasoning.length > 0) {
13547
14741
  return assistantReasoning;
13548
14742
  }
13549
- const argRecord = asRecord(args);
13550
- const detailRecord = asRecord(details);
14743
+ const argRecord = asRecord2(args);
14744
+ const detailRecord = asRecord2(details);
13551
14745
  const argReason = typeof argRecord.reasoning === "string" ? argRecord.reasoning.trim() : typeof argRecord.reason === "string" ? argRecord.reason.trim() : "";
13552
14746
  if (argReason.length > 0) {
13553
14747
  return argReason;
@@ -13570,7 +14764,7 @@ function formatLastRun(run) {
13570
14764
  function isAssistantMessage2(message) {
13571
14765
  return typeof message === "object" && message !== null && "role" in message && message.role === "assistant";
13572
14766
  }
13573
- function asRecord(value) {
14767
+ function asRecord2(value) {
13574
14768
  if (!value || typeof value !== "object" || Array.isArray(value)) {
13575
14769
  return {};
13576
14770
  }
@@ -13781,7 +14975,7 @@ function formatValue(value) {
13781
14975
  }
13782
14976
 
13783
14977
  // src/app/scenarios/claim-keys/deterministic-fixtures.ts
13784
- import { createHash as createHash2 } from "crypto";
14978
+ import { createHash as createHash3 } from "crypto";
13785
14979
  var DEFAULT_CONTEXT_WINDOW_TOKENS2 = 16e3;
13786
14980
  var DEFAULT_MAX_OUTPUT_TOKENS = 4e3;
13787
14981
  var EMBEDDING_DIMENSIONS = 1024;
@@ -13839,7 +15033,7 @@ function hashToVector(text2, dimensions) {
13839
15033
  const vector = [];
13840
15034
  let counter = 0;
13841
15035
  while (vector.length < dimensions) {
13842
- const block = createHash2("sha256").update(text2).update(String(counter)).digest();
15036
+ const block = createHash3("sha256").update(text2).update(String(counter)).digest();
13843
15037
  for (let offset = 0; offset + 4 <= block.length && vector.length < dimensions; offset += 4) {
13844
15038
  vector.push(block.readInt32LE(offset) / 2147483647);
13845
15039
  }
@@ -13880,8 +15074,8 @@ function isFixtureError(value) {
13880
15074
  }
13881
15075
 
13882
15076
  // src/app/scenarios/claim-keys/fixture-loader.ts
13883
- import { readFile } from "fs/promises";
13884
- import path17 from "path";
15077
+ import { readFile as readFile3 } from "fs/promises";
15078
+ import path19 from "path";
13885
15079
 
13886
15080
  // src/app/scenarios/claim-keys/validation/shared.ts
13887
15081
  var SUPPORTED_PROPOSAL_SCOPES = ["single_entry", "cluster"];
@@ -14211,12 +15405,12 @@ function readRequiredTrue(value, label, filePath) {
14211
15405
 
14212
15406
  // src/app/scenarios/claim-keys/validation/scenario-root.ts
14213
15407
  import { existsSync } from "fs";
14214
- import path16 from "path";
14215
- import { fileURLToPath as fileURLToPath2 } from "url";
15408
+ import path18 from "path";
15409
+ import { fileURLToPath } from "url";
14216
15410
  var SCENARIO_ROOT_SEGMENTS = ["tests", "scenarios", "claim-keys"];
14217
15411
  function getDefaultClaimKeyScenarioRoot(options = {}) {
14218
- const moduleDirectory = path16.dirname(fileURLToPath2(options.moduleUrl ?? import.meta.url));
14219
- 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]));
14220
15414
  for (const startDirectory of startDirectories) {
14221
15415
  const discovered = findScenarioRootFrom(startDirectory);
14222
15416
  if (discovered) {
@@ -14238,24 +15432,24 @@ function readOptionalRelativeFixturePath(value, label, filePath, rootDir) {
14238
15432
  return readRelativeFixturePath(value, label, filePath, rootDir);
14239
15433
  }
14240
15434
  function normalizeFixturePath(relativePath, rootDir, filePath, label) {
14241
- if (path16.isAbsolute(relativePath)) {
15435
+ if (path18.isAbsolute(relativePath)) {
14242
15436
  throw new Error(`Invalid scenario ${filePath}: ${label} must be relative to the scenario root.`);
14243
15437
  }
14244
- const resolved = path16.resolve(rootDir, relativePath);
14245
- const relative = path16.relative(rootDir, resolved);
14246
- 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)) {
14247
15441
  throw new Error(`Invalid scenario ${filePath}: ${label} must stay inside the scenario root.`);
14248
15442
  }
14249
- return relative.split(path16.sep).join("/");
15443
+ return relative.split(path18.sep).join("/");
14250
15444
  }
14251
15445
  function findScenarioRootFrom(startDirectory) {
14252
- let currentDirectory = path16.resolve(startDirectory);
15446
+ let currentDirectory = path18.resolve(startDirectory);
14253
15447
  while (true) {
14254
- const candidate = path16.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
15448
+ const candidate = path18.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
14255
15449
  if (existsSync(candidate)) {
14256
15450
  return candidate;
14257
15451
  }
14258
- const parentDirectory = path16.dirname(currentDirectory);
15452
+ const parentDirectory = path18.dirname(currentDirectory);
14259
15453
  if (parentDirectory === currentDirectory) {
14260
15454
  return void 0;
14261
15455
  }
@@ -14598,7 +15792,7 @@ async function loadSeedFixtureEntries(rootDir, relativePath) {
14598
15792
  if (!relativePath) {
14599
15793
  return null;
14600
15794
  }
14601
- const parsed = await readJsonFile(path17.join(rootDir, relativePath));
15795
+ const parsed = await readJsonFile(path19.join(rootDir, relativePath));
14602
15796
  const seedEntries = readSeedEntries(parsed, relativePath);
14603
15797
  if (!seedEntries) {
14604
15798
  throw new Error(`Seed fixture ${relativePath} must contain an array.`);
@@ -14661,7 +15855,7 @@ async function readArrayFixture(rootDir, relativePath) {
14661
15855
  if (!relativePath) {
14662
15856
  return null;
14663
15857
  }
14664
- const parsed = await readJsonFile(path17.join(rootDir, relativePath));
15858
+ const parsed = await readJsonFile(path19.join(rootDir, relativePath));
14665
15859
  if (!Array.isArray(parsed)) {
14666
15860
  throw new Error(`Fixture file ${relativePath} must contain a JSON array.`);
14667
15861
  }
@@ -14669,7 +15863,7 @@ async function readArrayFixture(rootDir, relativePath) {
14669
15863
  }
14670
15864
  async function readJsonFile(filePath) {
14671
15865
  try {
14672
- return JSON.parse(await readFile(filePath, "utf8"));
15866
+ return JSON.parse(await readFile3(filePath, "utf8"));
14673
15867
  } catch (error) {
14674
15868
  const message = error instanceof Error ? error.message : String(error);
14675
15869
  throw new Error(`Invalid fixture file ${filePath}: JSON parse failed - ${message}`, {
@@ -14714,8 +15908,8 @@ function readExtractionImportance(value, label, filePath) {
14714
15908
  }
14715
15909
 
14716
15910
  // src/app/scenarios/claim-keys/load-scenarios.ts
14717
- import { readdir, readFile as readFile2 } from "fs/promises";
14718
- import path18 from "path";
15911
+ import { readdir, readFile as readFile4 } from "fs/promises";
15912
+ import path20 from "path";
14719
15913
 
14720
15914
  // src/app/scenarios/claim-keys/validation/expectations.ts
14721
15915
  var EXPECTATION_KEYS = /* @__PURE__ */ new Set(["warnings", "rows", "rowCount", "proposals", "storeResult", "surgeonSummary"]);
@@ -14977,20 +16171,20 @@ function validateClaimKeyScenario(input, filePath, rootDir = getDefaultClaimKeyS
14977
16171
  async function discoverScenarioFiles(rootDir) {
14978
16172
  const files = [];
14979
16173
  for (const kind of SUPPORTED_KINDS2) {
14980
- const directory = path18.join(rootDir, kind);
16174
+ const directory = path20.join(rootDir, kind);
14981
16175
  const entries = await readdir(directory, { withFileTypes: true });
14982
16176
  for (const entry of entries) {
14983
16177
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
14984
16178
  continue;
14985
16179
  }
14986
- files.push(path18.join(directory, entry.name));
16180
+ files.push(path20.join(directory, entry.name));
14987
16181
  }
14988
16182
  }
14989
16183
  return files.sort();
14990
16184
  }
14991
16185
  async function readScenarioJsonFile(filePath) {
14992
16186
  try {
14993
- return JSON.parse(await readFile2(filePath, "utf8"));
16187
+ return JSON.parse(await readFile4(filePath, "utf8"));
14994
16188
  } catch (error) {
14995
16189
  const message = error instanceof Error ? error.message : String(error);
14996
16190
  throw new Error(`Invalid scenario ${filePath}: JSON parse failed - ${message}`, {
@@ -15001,11 +16195,11 @@ async function readScenarioJsonFile(filePath) {
15001
16195
 
15002
16196
  // src/app/scenarios/claim-keys/sandbox.ts
15003
16197
  import { mkdir, rm } from "fs/promises";
15004
- import path19 from "path";
16198
+ import path21 from "path";
15005
16199
  var SANDBOX_DB_FILENAME = "knowledge.db";
15006
16200
  async function createClaimKeyScenarioSandbox(root) {
15007
- const resolvedRoot = path19.resolve(root);
15008
- const dbPath = path19.join(resolvedRoot, SANDBOX_DB_FILENAME);
16201
+ const resolvedRoot = path21.resolve(root);
16202
+ const dbPath = path21.join(resolvedRoot, SANDBOX_DB_FILENAME);
15009
16203
  await mkdir(resolvedRoot, { recursive: true });
15010
16204
  await removeDatabaseFiles(dbPath);
15011
16205
  const database = await createDatabase(dbPath);
@@ -15196,7 +16390,7 @@ async function listClaimKeyScenariosRuntime(options = {}) {
15196
16390
  }
15197
16391
  async function runClaimKeyScenariosRuntime(options = {}) {
15198
16392
  const runId = buildRunId();
15199
- const artifactRoot = path20.resolve(DEFAULT_ARTIFACT_ROOT, runId);
16393
+ const artifactRoot = path22.resolve(DEFAULT_ARTIFACT_ROOT, runId);
15200
16394
  await mkdir2(artifactRoot, { recursive: true });
15201
16395
  let scenarios;
15202
16396
  try {
@@ -15248,11 +16442,11 @@ function filterClaimKeyScenarios(scenarios, options) {
15248
16442
  }
15249
16443
  async function runOneClaimKeyScenario(scenario, options) {
15250
16444
  const startedAt = Date.now();
15251
- const scenarioArtifactRoot = path20.join(options.artifactRoot, scenario.id);
15252
- const sandboxRoot = path20.join(scenarioArtifactRoot, "sandbox");
16445
+ const scenarioArtifactRoot = path22.join(options.artifactRoot, scenario.id);
16446
+ const sandboxRoot = path22.join(scenarioArtifactRoot, "sandbox");
15253
16447
  const warnings = [];
15254
16448
  await mkdir2(scenarioArtifactRoot, { recursive: true });
15255
- await writeJson(path20.join(scenarioArtifactRoot, "scenario.json"), scenario);
16449
+ await writeJson(path22.join(scenarioArtifactRoot, "scenario.json"), scenario);
15256
16450
  let actual = {
15257
16451
  warnings: [],
15258
16452
  rows: [],
@@ -15365,7 +16559,7 @@ async function runIngestScenario(scenario, database, warnings, rootDir) {
15365
16559
  throw new Error(`Scenario ${scenario.id} is missing extraction fixture responses.`);
15366
16560
  }
15367
16561
  const result = await ingestPath(
15368
- path20.join(rootDir, scenario.input.transcriptFile),
16562
+ path22.join(rootDir, scenario.input.transcriptFile),
15369
16563
  {
15370
16564
  files: localTranscriptFiles,
15371
16565
  transcript: openClawTranscriptParser,
@@ -15507,20 +16701,20 @@ async function loadLatestSurgeonProposals(database, runId) {
15507
16701
  }));
15508
16702
  }
15509
16703
  async function writeScenarioArtifacts(scenarioArtifactRoot, actual, assertionResults, diffSummary) {
15510
- await writeJson(path20.join(scenarioArtifactRoot, "actual.json"), actual);
15511
- await writeJson(path20.join(scenarioArtifactRoot, "diff.json"), {
16704
+ await writeJson(path22.join(scenarioArtifactRoot, "actual.json"), actual);
16705
+ await writeJson(path22.join(scenarioArtifactRoot, "diff.json"), {
15512
16706
  assertions: assertionResults,
15513
16707
  diffSummary
15514
16708
  });
15515
- await writeJson(path20.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
16709
+ await writeJson(path22.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
15516
16710
  if (actual.storeResult) {
15517
- await writeJson(path20.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
16711
+ await writeJson(path22.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
15518
16712
  }
15519
16713
  if (actual.surgeonSummary) {
15520
- await writeJson(path20.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
16714
+ await writeJson(path22.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
15521
16715
  }
15522
16716
  if (actual.proposals.length > 0) {
15523
- await writeJson(path20.join(scenarioArtifactRoot, "proposals.json"), actual.proposals);
16717
+ await writeJson(path22.join(scenarioArtifactRoot, "proposals.json"), actual.proposals);
15524
16718
  }
15525
16719
  }
15526
16720
  async function writeJson(filePath, value) {
@@ -15652,8 +16846,8 @@ import { InvalidArgumentError as InvalidArgumentError6, Option as Option5 } from
15652
16846
 
15653
16847
  // src/app/surgeon/runtime.ts
15654
16848
  import { copyFile, mkdir as mkdir3 } from "fs/promises";
15655
- import path21 from "path";
15656
- import { fileURLToPath as fileURLToPath3 } from "url";
16849
+ import path23 from "path";
16850
+ import { fileURLToPath as fileURLToPath2 } from "url";
15657
16851
  import { getModel as getModel2 } from "@earendil-works/pi-ai";
15658
16852
  var DEFAULT_SURGEON_PROVIDER = "openai";
15659
16853
  var DEFAULT_SURGEON_MODEL = "gpt-5.4-mini";
@@ -15897,7 +17091,7 @@ async function backupDatabaseFile(dbPath) {
15897
17091
  }
15898
17092
  const sourcePath = resolveFilesystemPath(dbPath);
15899
17093
  const backupPath = `${sourcePath}.surgeon-backup-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-")}`;
15900
- await mkdir3(path21.dirname(backupPath), { recursive: true });
17094
+ await mkdir3(path23.dirname(backupPath), { recursive: true });
15901
17095
  await copyFile(sourcePath, backupPath);
15902
17096
  await copySidecarIfPresent(`${sourcePath}-wal`, `${backupPath}-wal`);
15903
17097
  await copySidecarIfPresent(`${sourcePath}-shm`, `${backupPath}-shm`);
@@ -15915,12 +17109,12 @@ async function copySidecarIfPresent(sourcePath, targetPath) {
15915
17109
  }
15916
17110
  function resolveFilesystemPath(value) {
15917
17111
  if (!value.startsWith("file:")) {
15918
- return path21.resolve(value);
17112
+ return path23.resolve(value);
15919
17113
  }
15920
17114
  try {
15921
- return fileURLToPath3(value);
17115
+ return fileURLToPath2(value);
15922
17116
  } catch {
15923
- return path21.resolve(value.slice("file:".length));
17117
+ return path23.resolve(value.slice("file:".length));
15924
17118
  }
15925
17119
  }
15926
17120
  function isMissingFileError2(error) {