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