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/CHANGELOG.md +25 -0
- package/README.md +7 -5
- package/dist/adapters/skeln/index.d.ts +1 -2
- package/dist/adapters/skeln/index.js +2629 -85
- package/dist/{chunk-UEGURBBW.js → chunk-KH52KJSJ.js} +1 -1
- package/dist/{chunk-TMDNFBBC.js → chunk-M5M65AYP.js} +1 -2
- package/dist/{chunk-6HY5F5FE.js → chunk-RYMSM3OS.js} +10 -8
- package/dist/{chunk-FMQTRTWE.js → chunk-Z5X7T4QZ.js} +4 -68
- package/dist/{ports-CpzWESmZ.d.ts → claim-slot-policy-CQ-h0GaV.d.ts} +10 -55
- package/dist/cli.js +1333 -136
- package/dist/core/recall/index.d.ts +2 -3
- package/dist/internal-eval-server.js +3 -4
- package/dist/internal-recall-eval-server.js +3 -4
- package/package.json +4 -4
- package/dist/adapters/openclaw/index.d.ts +0 -32
- package/dist/adapters/openclaw/index.js +0 -3112
- package/dist/chunk-GELCEVFA.js +0 -14
- package/dist/chunk-KL6X2E3I.js +0 -2660
- package/dist/chunk-ZAX3YSTU.js +0 -1207
- package/dist/claim-slot-policy-CdrW_1l4.d.ts +0 -13
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-
|
|
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-
|
|
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
|
|
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
|
|
2215
|
-
if (
|
|
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
|
|
2369
|
-
import
|
|
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
|
|
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) =>
|
|
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
|
|
2480
|
-
import
|
|
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 =
|
|
2484
|
-
const
|
|
2485
|
-
if (
|
|
2486
|
-
return matchesOpenClawTranscriptFile(
|
|
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
|
|
2489
|
-
return entries.filter((entry) => entry.isFile() && matchesOpenClawTranscriptFile(entry.name)).map((entry) =>
|
|
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 =
|
|
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
|
|
2752
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
2926
|
-
import
|
|
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 =
|
|
2930
|
-
const
|
|
2931
|
-
if (
|
|
2932
|
-
return matchesProcedureFileName(
|
|
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
|
|
2936
|
-
return entries.filter((entry) => entry.isFile() && matchesProcedureFileName(entry.name)).map((entry) =>
|
|
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
|
|
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 =
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
4743
|
+
clack4.log.step(buildSkippedMessage(path11.basename(result.file)));
|
|
3552
4744
|
} else {
|
|
3553
|
-
clack4.log.step(buildSkippedMessage(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
5233
|
+
return path12.join(os.homedir(), trimmed.slice(2));
|
|
4042
5234
|
}
|
|
4043
5235
|
if (trimmed.startsWith("~\\")) {
|
|
4044
|
-
return
|
|
5236
|
+
return path12.join(os.homedir(), trimmed.slice(2));
|
|
4045
5237
|
}
|
|
4046
|
-
return
|
|
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
|
|
5120
|
-
import
|
|
5121
|
-
var OPENCLAW_PLUGIN_PACKAGE = "@agenr/
|
|
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 =
|
|
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 =
|
|
6422
|
+
const openclawConfigPath = path13.join(stateDir, "openclaw.json");
|
|
5229
6423
|
let root = {};
|
|
5230
6424
|
try {
|
|
5231
|
-
const raw = await
|
|
6425
|
+
const raw = await fs9.readFile(openclawConfigPath, "utf8");
|
|
5232
6426
|
const parsed = JSON.parse(raw);
|
|
5233
|
-
if (
|
|
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
|
|
5258
|
-
await
|
|
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
|
|
6457
|
+
return path13.dirname(dbPath) !== path13.dirname(configPath);
|
|
5264
6458
|
}
|
|
5265
|
-
function
|
|
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 (
|
|
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
|
|
6484
|
+
import fs10 from "fs";
|
|
5291
6485
|
import os2 from "os";
|
|
5292
|
-
import
|
|
6486
|
+
import path14 from "path";
|
|
5293
6487
|
function resolveDefaultOpenClawStateDir() {
|
|
5294
|
-
return
|
|
6488
|
+
return path14.join(os2.homedir(), ".openclaw");
|
|
5295
6489
|
}
|
|
5296
|
-
function detectOpenClawInstallation(env = process.env, existsSyncFn =
|
|
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:
|
|
5304
|
-
sessionsRoot:
|
|
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
|
|
5315
|
-
import
|
|
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
|
|
6532
|
+
let stat2;
|
|
5339
6533
|
try {
|
|
5340
|
-
|
|
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 +=
|
|
5350
|
-
if (
|
|
6543
|
+
result.totalSizeBytes += stat2.size;
|
|
6544
|
+
if (stat2.mtimeMs >= cutoffMs) {
|
|
5351
6545
|
result.recentFiles.push(filePath);
|
|
5352
|
-
result.recentSizeBytes +=
|
|
6546
|
+
result.recentSizeBytes += stat2.size;
|
|
5353
6547
|
}
|
|
5354
6548
|
}
|
|
5355
6549
|
return result;
|
|
5356
6550
|
}
|
|
5357
6551
|
function isSessionTranscriptPath(filePath) {
|
|
5358
|
-
return filePath.split(
|
|
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/
|
|
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
|
|
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
|
|
7393
|
-
import
|
|
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
|
|
10120
|
-
import
|
|
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
|
-
|
|
10329
|
-
|
|
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 (
|
|
12879
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
13547
|
-
const detailRecord =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
14216
|
-
const startDirectories = Array.from(/* @__PURE__ */ new Set([
|
|
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 (
|
|
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 =
|
|
14242
|
-
const relative =
|
|
14243
|
-
if (relative.startsWith("..") ||
|
|
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(
|
|
15443
|
+
return relative.split(path18.sep).join("/");
|
|
14247
15444
|
}
|
|
14248
15445
|
function findScenarioRootFrom(startDirectory) {
|
|
14249
|
-
let currentDirectory =
|
|
15446
|
+
let currentDirectory = path18.resolve(startDirectory);
|
|
14250
15447
|
while (true) {
|
|
14251
|
-
const candidate =
|
|
15448
|
+
const candidate = path18.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
|
|
14252
15449
|
if (existsSync(candidate)) {
|
|
14253
15450
|
return candidate;
|
|
14254
15451
|
}
|
|
14255
|
-
const parentDirectory =
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
14715
|
-
import
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
16198
|
+
import path21 from "path";
|
|
15002
16199
|
var SANDBOX_DB_FILENAME = "knowledge.db";
|
|
15003
16200
|
async function createClaimKeyScenarioSandbox(root) {
|
|
15004
|
-
const resolvedRoot =
|
|
15005
|
-
const dbPath =
|
|
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 =
|
|
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 =
|
|
15249
|
-
const sandboxRoot =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
15508
|
-
await writeJson(
|
|
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(
|
|
16709
|
+
await writeJson(path22.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
|
|
15513
16710
|
if (actual.storeResult) {
|
|
15514
|
-
await writeJson(
|
|
16711
|
+
await writeJson(path22.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
|
|
15515
16712
|
}
|
|
15516
16713
|
if (actual.surgeonSummary) {
|
|
15517
|
-
await writeJson(
|
|
16714
|
+
await writeJson(path22.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
|
|
15518
16715
|
}
|
|
15519
16716
|
if (actual.proposals.length > 0) {
|
|
15520
|
-
await writeJson(
|
|
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
|
|
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(
|
|
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
|
|
17112
|
+
return path23.resolve(value);
|
|
15916
17113
|
}
|
|
15917
17114
|
try {
|
|
15918
17115
|
return fileURLToPath2(value);
|
|
15919
17116
|
} catch {
|
|
15920
|
-
return
|
|
17117
|
+
return path23.resolve(value.slice("file:".length));
|
|
15921
17118
|
}
|
|
15922
17119
|
}
|
|
15923
17120
|
function isMissingFileError2(error) {
|