agenr 1.9.2 → 2.0.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 +40 -0
- package/README.md +25 -15
- package/dist/adapters/openclaw/index.js +132 -10
- package/dist/{chunk-I6A6DPNF.js → chunk-XD3446YW.js} +2 -2
- package/dist/{chunk-EMRMV2QR.js → chunk-Y2BC7RCE.js} +1347 -110
- package/dist/chunk-ZYADFKX3.js +115 -0
- package/dist/cli.js +767 -252
- package/dist/core/recall/index.js +1 -2
- package/dist/internal-recall-eval-server.js +131 -12
- package/package.json +5 -4
- package/dist/chunk-ETQPUJGS.js +0 -0
- package/dist/{chunk-GUDCFFRV.js → chunk-MEHOGUZE.js} +175 -175
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
composeProcedureRecallText,
|
|
4
|
+
computeProcedureRevisionHash,
|
|
5
|
+
computeProcedureSourceHash
|
|
6
|
+
} from "./chunk-ZYADFKX3.js";
|
|
3
7
|
import {
|
|
4
8
|
applyClaimExtractionResultToEntry,
|
|
5
9
|
backfillEpisodeEmbeddings,
|
|
@@ -27,7 +31,7 @@ import {
|
|
|
27
31
|
tokenizeGroundingText,
|
|
28
32
|
validateEntriesWithIndexes,
|
|
29
33
|
validateSupersessionRules
|
|
30
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-XD3446YW.js";
|
|
31
35
|
import {
|
|
32
36
|
DEFAULT_CLAIM_EXTRACTION_CONCURRENCY,
|
|
33
37
|
DEFAULT_SURGEON_CONTEXT_LIMIT,
|
|
@@ -66,6 +70,7 @@ import {
|
|
|
66
70
|
mapEntryRow,
|
|
67
71
|
mergeExplicitClaimKeyMetadata,
|
|
68
72
|
normalizeManualClaimKeyUpdate,
|
|
73
|
+
parseAndNormalizeProcedureYaml,
|
|
69
74
|
projectClaimCentricRecallEntry,
|
|
70
75
|
readBoolean,
|
|
71
76
|
readConfig,
|
|
@@ -86,7 +91,7 @@ import {
|
|
|
86
91
|
updateEntry,
|
|
87
92
|
validateTemporalValidityRange,
|
|
88
93
|
writeConfig
|
|
89
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-Y2BC7RCE.js";
|
|
90
95
|
import {
|
|
91
96
|
compactClaimKey,
|
|
92
97
|
describeClaimKeyNormalizationFailure,
|
|
@@ -97,7 +102,7 @@ import {
|
|
|
97
102
|
normalizeClaimKeySegment,
|
|
98
103
|
recall,
|
|
99
104
|
resolveClaimSlotPolicy
|
|
100
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-MEHOGUZE.js";
|
|
101
106
|
|
|
102
107
|
// src/cli/main.ts
|
|
103
108
|
import { Command } from "commander";
|
|
@@ -230,8 +235,8 @@ function formatUnknownError(error) {
|
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
// src/cli/commands/ingest.ts
|
|
233
|
-
import
|
|
234
|
-
import * as
|
|
238
|
+
import path10 from "path";
|
|
239
|
+
import * as clack4 from "@clack/prompts";
|
|
235
240
|
|
|
236
241
|
// src/core/ingestion/parser.ts
|
|
237
242
|
var IMPORTANCE_TIER_MAP = {
|
|
@@ -2965,20 +2970,20 @@ function registerIngestEpisodesCommand(parent) {
|
|
|
2965
2970
|
embedding: embeddingSetup.port,
|
|
2966
2971
|
createSummaryLlm: () => createEpisodeIngestSummaryLlm(provider, modelId, llmApiKey)
|
|
2967
2972
|
};
|
|
2968
|
-
const
|
|
2969
|
-
|
|
2973
|
+
const spinner6 = commandInput.verbose ? null : clack2.spinner();
|
|
2974
|
+
spinner6?.start(`Generating episodes... (0/${plan.candidates.length})`);
|
|
2970
2975
|
const execution = await executeEpisodeIngestPlan(plan, executionPorts, {
|
|
2971
2976
|
concurrency: commandInput.concurrency,
|
|
2972
2977
|
genVersion: CLI_EPISODE_GENERATOR_VERSION,
|
|
2973
2978
|
onProgress: (completed, total, session) => {
|
|
2974
|
-
if (
|
|
2975
|
-
|
|
2979
|
+
if (spinner6) {
|
|
2980
|
+
spinner6.message(`Generating episodes... (${completed}/${total})`);
|
|
2976
2981
|
} else {
|
|
2977
2982
|
reportEpisodeProgress(completed, total, session);
|
|
2978
2983
|
}
|
|
2979
2984
|
}
|
|
2980
2985
|
});
|
|
2981
|
-
|
|
2986
|
+
spinner6?.stop("Episode ingest complete.");
|
|
2982
2987
|
if (!commandInput.verbose) {
|
|
2983
2988
|
for (const session of execution.sessions.filter((result) => result.action === "failed")) {
|
|
2984
2989
|
clack2.log.error(formatEpisodeProgressLine(void 0, void 0, session));
|
|
@@ -3054,19 +3059,19 @@ async function runEpisodeEmbeddingBackfill(params) {
|
|
|
3054
3059
|
clack2.outro(`Dry run complete: ${missingEpisodes.length} ${pluralize(missingEpisodes.length, "episode")} need embeddings.`);
|
|
3055
3060
|
return;
|
|
3056
3061
|
}
|
|
3057
|
-
const
|
|
3058
|
-
|
|
3062
|
+
const spinner6 = params.options.verbose ? null : clack2.spinner();
|
|
3063
|
+
spinner6?.start(`Embedding episodes... (0/${missingEpisodes.length})`);
|
|
3059
3064
|
const result = await backfillEpisodeEmbeddings(ports, {
|
|
3060
3065
|
concurrency: params.options.concurrency,
|
|
3061
3066
|
onProgress: (completed, total, episode, status) => {
|
|
3062
|
-
if (
|
|
3063
|
-
|
|
3067
|
+
if (spinner6) {
|
|
3068
|
+
spinner6.message(`Embedding episodes... (${completed}/${total})`);
|
|
3064
3069
|
} else {
|
|
3065
3070
|
clack2.log.step(`${completed}/${total} ${episode.sourceId ?? episode.id}: ${status}`);
|
|
3066
3071
|
}
|
|
3067
3072
|
}
|
|
3068
3073
|
});
|
|
3069
|
-
|
|
3074
|
+
spinner6?.stop("Episode embedding backfill complete.");
|
|
3070
3075
|
const estimatedCost = estimateEpisodeEmbeddingCost(result.estimatedInputTokens, embeddingModel);
|
|
3071
3076
|
const summaryParts = [`Done: ${result.embedded} ${pluralize(result.embedded, "episode")} embedded.`];
|
|
3072
3077
|
if (result.failed > 0) {
|
|
@@ -3285,6 +3290,503 @@ function parseEpisodeIngestConcurrency(value) {
|
|
|
3285
3290
|
return parseIntegerInRange(value, "Concurrency", MIN_EPISODE_INGEST_CONCURRENCY, MAX_EPISODE_INGEST_CONCURRENCY);
|
|
3286
3291
|
}
|
|
3287
3292
|
|
|
3293
|
+
// src/cli/commands/ingest-procedures.ts
|
|
3294
|
+
import path9 from "path";
|
|
3295
|
+
import * as clack3 from "@clack/prompts";
|
|
3296
|
+
import "commander";
|
|
3297
|
+
|
|
3298
|
+
// src/adapters/files/procedure-files.ts
|
|
3299
|
+
import fs6 from "fs/promises";
|
|
3300
|
+
import path8 from "path";
|
|
3301
|
+
var PROCEDURE_FILE_PATTERN = /^.+\.ya?ml$/iu;
|
|
3302
|
+
async function discoverProcedureFiles(targetPath, options = {}) {
|
|
3303
|
+
const resolvedTargetPath = path8.resolve(targetPath);
|
|
3304
|
+
const stat = await fs6.stat(resolvedTargetPath);
|
|
3305
|
+
if (stat.isFile()) {
|
|
3306
|
+
return matchesProcedureFileName(path8.basename(resolvedTargetPath)) ? [resolvedTargetPath] : [];
|
|
3307
|
+
}
|
|
3308
|
+
const recursive = options.recursive ?? true;
|
|
3309
|
+
const entries = recursive ? await fs6.readdir(resolvedTargetPath, { recursive: true, withFileTypes: true }) : await fs6.readdir(resolvedTargetPath, { withFileTypes: true });
|
|
3310
|
+
return entries.filter((entry) => entry.isFile() && matchesProcedureFileName(entry.name)).map((entry) => path8.resolve(entry.parentPath ?? resolvedTargetPath, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
3311
|
+
}
|
|
3312
|
+
async function readProcedureFile(filePath) {
|
|
3313
|
+
return fs6.readFile(filePath, "utf-8");
|
|
3314
|
+
}
|
|
3315
|
+
var LocalProcedureFiles = class {
|
|
3316
|
+
/**
|
|
3317
|
+
* Discovers procedure YAML files from a target file or directory path.
|
|
3318
|
+
*
|
|
3319
|
+
* @param targetPath - File or directory to inspect for procedure files.
|
|
3320
|
+
* @param options - Optional discovery flags.
|
|
3321
|
+
* @returns Sorted absolute procedure file paths.
|
|
3322
|
+
*/
|
|
3323
|
+
async discoverFiles(targetPath, options) {
|
|
3324
|
+
return discoverProcedureFiles(targetPath, options);
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Reads one raw procedure YAML file from disk.
|
|
3328
|
+
*
|
|
3329
|
+
* @param filePath - Absolute procedure file path.
|
|
3330
|
+
* @returns Raw UTF-8 procedure source text.
|
|
3331
|
+
*/
|
|
3332
|
+
async readFile(filePath) {
|
|
3333
|
+
return readProcedureFile(filePath);
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
var localProcedureFiles = new LocalProcedureFiles();
|
|
3337
|
+
function matchesProcedureFileName(fileName) {
|
|
3338
|
+
return PROCEDURE_FILE_PATTERN.test(fileName.trim());
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
// src/app/procedures/sync/service/execute.ts
|
|
3342
|
+
import { randomUUID } from "crypto";
|
|
3343
|
+
async function executeProcedureSync(plan, ports) {
|
|
3344
|
+
const invalidItems = plan.items.filter((item) => item.action === "invalid");
|
|
3345
|
+
if (invalidItems.length > 0) {
|
|
3346
|
+
throw new Error(formatInvalidPlanError(invalidItems));
|
|
3347
|
+
}
|
|
3348
|
+
const itemsNeedingEmbeddings = plan.items.filter(
|
|
3349
|
+
(item) => item.action === "create" || item.action === "supersede"
|
|
3350
|
+
);
|
|
3351
|
+
const embeddings = itemsNeedingEmbeddings.length > 0 ? await ports.embedding.embed(itemsNeedingEmbeddings.map((item) => item.candidate.recallText)) : [];
|
|
3352
|
+
if (embeddings.length !== itemsNeedingEmbeddings.length) {
|
|
3353
|
+
throw new Error(`Procedure embedding count mismatch: expected ${itemsNeedingEmbeddings.length}, received ${embeddings.length}.`);
|
|
3354
|
+
}
|
|
3355
|
+
const embeddingByFilePath = /* @__PURE__ */ new Map();
|
|
3356
|
+
itemsNeedingEmbeddings.forEach((item, index) => {
|
|
3357
|
+
const embedding = embeddings[index];
|
|
3358
|
+
if (!embedding) {
|
|
3359
|
+
throw new Error(`Missing embedding for procedure file ${item.candidate.filePath}.`);
|
|
3360
|
+
}
|
|
3361
|
+
embeddingByFilePath.set(item.candidate.filePath, embedding);
|
|
3362
|
+
});
|
|
3363
|
+
const items = await ports.db.withTransaction(async (db) => {
|
|
3364
|
+
const executionItems = [];
|
|
3365
|
+
for (const item of plan.items) {
|
|
3366
|
+
switch (item.action) {
|
|
3367
|
+
case "create": {
|
|
3368
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3369
|
+
const stored = await db.upsertProcedure(
|
|
3370
|
+
buildProcedureRecord({
|
|
3371
|
+
candidate: item.candidate,
|
|
3372
|
+
id: randomUUID(),
|
|
3373
|
+
createdAt: now,
|
|
3374
|
+
updatedAt: now,
|
|
3375
|
+
embedding: embeddingByFilePath.get(item.candidate.filePath),
|
|
3376
|
+
retired: false
|
|
3377
|
+
})
|
|
3378
|
+
);
|
|
3379
|
+
executionItems.push({
|
|
3380
|
+
action: "created",
|
|
3381
|
+
filePath: item.candidate.filePath,
|
|
3382
|
+
procedureKey: item.candidate.procedure.procedure_key,
|
|
3383
|
+
procedureId: stored.id
|
|
3384
|
+
});
|
|
3385
|
+
break;
|
|
3386
|
+
}
|
|
3387
|
+
case "update_source_only": {
|
|
3388
|
+
const stored = await db.upsertProcedure(
|
|
3389
|
+
buildProcedureRecord({
|
|
3390
|
+
candidate: item.candidate,
|
|
3391
|
+
existing: item.existing,
|
|
3392
|
+
id: item.existing.id,
|
|
3393
|
+
createdAt: item.existing.created_at,
|
|
3394
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3395
|
+
embedding: item.existing.embedding,
|
|
3396
|
+
retired: false
|
|
3397
|
+
})
|
|
3398
|
+
);
|
|
3399
|
+
executionItems.push({
|
|
3400
|
+
action: "updated_source_only",
|
|
3401
|
+
filePath: item.candidate.filePath,
|
|
3402
|
+
procedureKey: item.candidate.procedure.procedure_key,
|
|
3403
|
+
procedureId: stored.id
|
|
3404
|
+
});
|
|
3405
|
+
break;
|
|
3406
|
+
}
|
|
3407
|
+
case "supersede": {
|
|
3408
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3409
|
+
const replacementId = randomUUID();
|
|
3410
|
+
const staged = buildProcedureRecord({
|
|
3411
|
+
candidate: item.candidate,
|
|
3412
|
+
id: replacementId,
|
|
3413
|
+
createdAt: now,
|
|
3414
|
+
updatedAt: now,
|
|
3415
|
+
embedding: embeddingByFilePath.get(item.candidate.filePath),
|
|
3416
|
+
retired: true
|
|
3417
|
+
});
|
|
3418
|
+
await db.upsertProcedure(staged);
|
|
3419
|
+
const superseded = await db.supersedeProcedure(item.existing.id, replacementId, "procedure revision updated");
|
|
3420
|
+
if (!superseded) {
|
|
3421
|
+
throw new Error(`Failed to supersede active procedure ${item.existing.id} for ${item.candidate.procedure.procedure_key}.`);
|
|
3422
|
+
}
|
|
3423
|
+
const activated = await db.upsertProcedure({
|
|
3424
|
+
...staged,
|
|
3425
|
+
retired: false,
|
|
3426
|
+
retired_at: void 0,
|
|
3427
|
+
retired_reason: void 0,
|
|
3428
|
+
superseded_by: void 0,
|
|
3429
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3430
|
+
});
|
|
3431
|
+
executionItems.push({
|
|
3432
|
+
action: "superseded",
|
|
3433
|
+
filePath: item.candidate.filePath,
|
|
3434
|
+
procedureKey: item.candidate.procedure.procedure_key,
|
|
3435
|
+
procedureId: activated.id,
|
|
3436
|
+
previousProcedureId: item.existing.id
|
|
3437
|
+
});
|
|
3438
|
+
break;
|
|
3439
|
+
}
|
|
3440
|
+
case "unchanged":
|
|
3441
|
+
executionItems.push({
|
|
3442
|
+
action: "unchanged",
|
|
3443
|
+
filePath: item.candidate.filePath,
|
|
3444
|
+
procedureKey: item.candidate.procedure.procedure_key,
|
|
3445
|
+
procedureId: item.existing.id
|
|
3446
|
+
});
|
|
3447
|
+
break;
|
|
3448
|
+
case "invalid":
|
|
3449
|
+
throw new Error(`Invalid procedure plan item for ${item.filePath}: ${item.error}`);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
return executionItems;
|
|
3453
|
+
});
|
|
3454
|
+
return {
|
|
3455
|
+
plan,
|
|
3456
|
+
items,
|
|
3457
|
+
totals: summarizeExecution(items)
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
function buildProcedureRecord(params) {
|
|
3461
|
+
const { candidate, existing } = params;
|
|
3462
|
+
return {
|
|
3463
|
+
id: params.id,
|
|
3464
|
+
...candidate.procedure,
|
|
3465
|
+
recall_text: candidate.recallText,
|
|
3466
|
+
revision_hash: candidate.revisionHash,
|
|
3467
|
+
source_hash: candidate.sourceHash,
|
|
3468
|
+
source_file: candidate.filePath,
|
|
3469
|
+
embedding: params.embedding ?? existing?.embedding,
|
|
3470
|
+
retired: params.retired,
|
|
3471
|
+
retired_at: existing?.retired_at,
|
|
3472
|
+
retired_reason: existing?.retired_reason,
|
|
3473
|
+
superseded_by: existing?.superseded_by,
|
|
3474
|
+
created_at: params.createdAt,
|
|
3475
|
+
updated_at: params.updatedAt
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
function summarizeExecution(items) {
|
|
3479
|
+
return items.reduce(
|
|
3480
|
+
(totals, item) => {
|
|
3481
|
+
switch (item.action) {
|
|
3482
|
+
case "created":
|
|
3483
|
+
totals.created += 1;
|
|
3484
|
+
break;
|
|
3485
|
+
case "updated_source_only":
|
|
3486
|
+
totals.updatedSourceOnly += 1;
|
|
3487
|
+
break;
|
|
3488
|
+
case "superseded":
|
|
3489
|
+
totals.superseded += 1;
|
|
3490
|
+
break;
|
|
3491
|
+
case "unchanged":
|
|
3492
|
+
totals.unchanged += 1;
|
|
3493
|
+
break;
|
|
3494
|
+
}
|
|
3495
|
+
return totals;
|
|
3496
|
+
},
|
|
3497
|
+
{
|
|
3498
|
+
created: 0,
|
|
3499
|
+
updatedSourceOnly: 0,
|
|
3500
|
+
superseded: 0,
|
|
3501
|
+
unchanged: 0
|
|
3502
|
+
}
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3505
|
+
function formatInvalidPlanError(invalidItems) {
|
|
3506
|
+
const details = invalidItems.map((item) => `${item.filePath}: ${item.error}`).join(" | ");
|
|
3507
|
+
return `Procedure sync plan contains ${invalidItems.length} invalid file(s): ${details}`;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// src/app/procedures/sync/service/prepare.ts
|
|
3511
|
+
async function prepareProcedureSync(targetPath, ports) {
|
|
3512
|
+
const files = await ports.files.discoverFiles(targetPath);
|
|
3513
|
+
const discoveredItems = await Promise.all(files.map((filePath) => prepareCandidateForFile(filePath, ports)));
|
|
3514
|
+
const duplicateKeys = collectDuplicateProcedureKeys(discoveredItems);
|
|
3515
|
+
const planItems = [];
|
|
3516
|
+
for (const discoveredItem of discoveredItems) {
|
|
3517
|
+
if ("action" in discoveredItem) {
|
|
3518
|
+
planItems.push(discoveredItem);
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
3521
|
+
const duplicates = duplicateKeys.get(discoveredItem.procedure.procedure_key);
|
|
3522
|
+
if (duplicates && duplicates.length > 1) {
|
|
3523
|
+
planItems.push({
|
|
3524
|
+
action: "invalid",
|
|
3525
|
+
filePath: discoveredItem.filePath,
|
|
3526
|
+
error: formatDuplicateProcedureKeyError(discoveredItem.procedure.procedure_key, duplicates)
|
|
3527
|
+
});
|
|
3528
|
+
continue;
|
|
3529
|
+
}
|
|
3530
|
+
const existing = await ports.db.findActiveProcedureByKey(discoveredItem.procedure.procedure_key);
|
|
3531
|
+
planItems.push(classifyProcedureCandidate(discoveredItem, existing));
|
|
3532
|
+
}
|
|
3533
|
+
return {
|
|
3534
|
+
targetPath,
|
|
3535
|
+
files,
|
|
3536
|
+
items: planItems,
|
|
3537
|
+
totals: summarizePlan(planItems, files.length)
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
async function prepareCandidateForFile(filePath, ports) {
|
|
3541
|
+
try {
|
|
3542
|
+
const sourceText = await ports.files.readFile(filePath);
|
|
3543
|
+
const procedure = parseAndNormalizeProcedureYaml(sourceText, filePath);
|
|
3544
|
+
return {
|
|
3545
|
+
filePath,
|
|
3546
|
+
procedure,
|
|
3547
|
+
recallText: composeProcedureRecallText(procedure),
|
|
3548
|
+
revisionHash: computeProcedureRevisionHash(procedure),
|
|
3549
|
+
sourceHash: computeProcedureSourceHash(sourceText)
|
|
3550
|
+
};
|
|
3551
|
+
} catch (error) {
|
|
3552
|
+
return {
|
|
3553
|
+
action: "invalid",
|
|
3554
|
+
filePath,
|
|
3555
|
+
error: formatUnknownError3(error)
|
|
3556
|
+
};
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
function collectDuplicateProcedureKeys(items) {
|
|
3560
|
+
const filesByProcedureKey = /* @__PURE__ */ new Map();
|
|
3561
|
+
for (const item of items) {
|
|
3562
|
+
if ("action" in item) {
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
const files = filesByProcedureKey.get(item.procedure.procedure_key) ?? [];
|
|
3566
|
+
files.push(item.filePath);
|
|
3567
|
+
filesByProcedureKey.set(item.procedure.procedure_key, files);
|
|
3568
|
+
}
|
|
3569
|
+
return new Map(Array.from(filesByProcedureKey.entries()).filter(([, files]) => files.length > 1));
|
|
3570
|
+
}
|
|
3571
|
+
function classifyProcedureCandidate(candidate, existing) {
|
|
3572
|
+
if (!existing) {
|
|
3573
|
+
return {
|
|
3574
|
+
action: "create",
|
|
3575
|
+
candidate
|
|
3576
|
+
};
|
|
3577
|
+
}
|
|
3578
|
+
if (existing.revision_hash !== candidate.revisionHash) {
|
|
3579
|
+
return {
|
|
3580
|
+
action: "supersede",
|
|
3581
|
+
candidate,
|
|
3582
|
+
existing
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
if (existing.source_hash !== candidate.sourceHash || existing.source_file !== candidate.filePath) {
|
|
3586
|
+
return {
|
|
3587
|
+
action: "update_source_only",
|
|
3588
|
+
candidate,
|
|
3589
|
+
existing
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3592
|
+
return {
|
|
3593
|
+
action: "unchanged",
|
|
3594
|
+
candidate,
|
|
3595
|
+
existing
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
function summarizePlan(items, discoveredCount) {
|
|
3599
|
+
return items.reduce(
|
|
3600
|
+
(totals, item) => {
|
|
3601
|
+
switch (item.action) {
|
|
3602
|
+
case "create":
|
|
3603
|
+
totals.create += 1;
|
|
3604
|
+
break;
|
|
3605
|
+
case "update_source_only":
|
|
3606
|
+
totals.updateSourceOnly += 1;
|
|
3607
|
+
break;
|
|
3608
|
+
case "supersede":
|
|
3609
|
+
totals.supersede += 1;
|
|
3610
|
+
break;
|
|
3611
|
+
case "unchanged":
|
|
3612
|
+
totals.unchanged += 1;
|
|
3613
|
+
break;
|
|
3614
|
+
case "invalid":
|
|
3615
|
+
totals.invalid += 1;
|
|
3616
|
+
break;
|
|
3617
|
+
}
|
|
3618
|
+
return totals;
|
|
3619
|
+
},
|
|
3620
|
+
{
|
|
3621
|
+
discovered: discoveredCount,
|
|
3622
|
+
create: 0,
|
|
3623
|
+
updateSourceOnly: 0,
|
|
3624
|
+
supersede: 0,
|
|
3625
|
+
unchanged: 0,
|
|
3626
|
+
invalid: 0
|
|
3627
|
+
}
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
function formatDuplicateProcedureKeyError(procedureKey, filePaths) {
|
|
3631
|
+
return `Duplicate procedure_key "${procedureKey}" found in ${filePaths.join(", ")}.`;
|
|
3632
|
+
}
|
|
3633
|
+
function formatUnknownError3(error) {
|
|
3634
|
+
return error instanceof Error ? error.message : String(error);
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
// src/cli/commands/ingest-procedures.ts
|
|
3638
|
+
var DEFAULT_PROCEDURE_SYNC_PATH = "procedures";
|
|
3639
|
+
function registerIngestProceduresCommand(parent) {
|
|
3640
|
+
parent.command("procedures [path]").description("Sync repo-authored procedure YAML files into the knowledge database").option("--dry-run", "Discover, validate, normalize, and diff without writing").option("--verbose", "Show detailed per-file planning and execution output").action(async (targetPath, options) => {
|
|
3641
|
+
let database = null;
|
|
3642
|
+
const commandInput = normalizeIngestProceduresCommand(targetPath, options);
|
|
3643
|
+
setVerbose(commandInput.verbose);
|
|
3644
|
+
clack3.intro(banner());
|
|
3645
|
+
try {
|
|
3646
|
+
const config = readConfig();
|
|
3647
|
+
const dbPath = config.dbPath;
|
|
3648
|
+
const resolvedTargetPath = path9.resolve(commandInput.targetPath);
|
|
3649
|
+
database = await createDatabase(dbPath);
|
|
3650
|
+
if (commandInput.verbose) {
|
|
3651
|
+
clack3.log.step(`Preparing procedure sync for ${resolvedTargetPath}...`);
|
|
3652
|
+
}
|
|
3653
|
+
const plan = await prepareProcedureSync(commandInput.targetPath, {
|
|
3654
|
+
files: localProcedureFiles,
|
|
3655
|
+
db: database
|
|
3656
|
+
});
|
|
3657
|
+
if (plan.files.length === 0) {
|
|
3658
|
+
clack3.log.warn(`No procedure files found at ${resolvedTargetPath}.`);
|
|
3659
|
+
clack3.outro("Nothing to sync.");
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
printProcedureSyncSummary({
|
|
3663
|
+
targetPath: resolvedTargetPath,
|
|
3664
|
+
dbPath,
|
|
3665
|
+
dryRun: commandInput.dryRun,
|
|
3666
|
+
plan
|
|
3667
|
+
});
|
|
3668
|
+
if (commandInput.verbose) {
|
|
3669
|
+
printVerboseProcedurePlan(plan.items);
|
|
3670
|
+
}
|
|
3671
|
+
if (commandInput.dryRun) {
|
|
3672
|
+
if (plan.totals.invalid > 0) {
|
|
3673
|
+
process.exitCode = 1;
|
|
3674
|
+
}
|
|
3675
|
+
clack3.outro(`Dry run complete: ${formatProcedurePlanTail(plan)}.`);
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
if (plan.totals.invalid > 0) {
|
|
3679
|
+
throw new Error(`Procedure sync blocked: ${plan.totals.invalid} invalid file(s) must be fixed before writing.`);
|
|
3680
|
+
}
|
|
3681
|
+
const embedding = createEmbeddingClient(resolveEmbeddingApiKey(config), resolveEmbeddingModel(config));
|
|
3682
|
+
const spinner6 = commandInput.verbose ? null : clack3.spinner();
|
|
3683
|
+
spinner6?.start(`Syncing procedures... (${countPlannedWrites(plan.items)} writes planned)`);
|
|
3684
|
+
const execution = await executeProcedureSync(plan, {
|
|
3685
|
+
db: database,
|
|
3686
|
+
embedding
|
|
3687
|
+
});
|
|
3688
|
+
spinner6?.stop("Procedure sync complete.");
|
|
3689
|
+
if (commandInput.verbose) {
|
|
3690
|
+
printVerboseProcedureExecution(execution.items);
|
|
3691
|
+
}
|
|
3692
|
+
clack3.outro(`Done: ${formatProcedureExecutionTail(execution)}.`);
|
|
3693
|
+
} catch (error) {
|
|
3694
|
+
process.exitCode = 1;
|
|
3695
|
+
clack3.log.error(formatUnknownError4(error));
|
|
3696
|
+
clack3.outro(ui.error("Procedure sync failed"));
|
|
3697
|
+
} finally {
|
|
3698
|
+
await database?.close();
|
|
3699
|
+
}
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
function normalizeIngestProceduresCommand(targetPath, options) {
|
|
3703
|
+
return {
|
|
3704
|
+
targetPath: normalizeOptionalString3(targetPath) ?? DEFAULT_PROCEDURE_SYNC_PATH,
|
|
3705
|
+
verbose: options.verbose === true,
|
|
3706
|
+
dryRun: options.dryRun === true
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
function printProcedureSyncSummary(params) {
|
|
3710
|
+
const lines = [
|
|
3711
|
+
formatLabel("Target", params.targetPath),
|
|
3712
|
+
formatLabel("Database", params.dbPath),
|
|
3713
|
+
formatLabel("Files", `${params.plan.totals.discovered} ${pluralize2(params.plan.totals.discovered, "file")} discovered`),
|
|
3714
|
+
formatLabel(
|
|
3715
|
+
"Plan",
|
|
3716
|
+
`${params.plan.totals.create} create | ${params.plan.totals.updateSourceOnly} source update | ${params.plan.totals.supersede} supersede | ${params.plan.totals.unchanged} unchanged | ${params.plan.totals.invalid} invalid`
|
|
3717
|
+
)
|
|
3718
|
+
];
|
|
3719
|
+
if (params.dryRun) {
|
|
3720
|
+
lines.push(formatLabel("Mode", "dry run"));
|
|
3721
|
+
}
|
|
3722
|
+
clack3.log.info(lines.join("\n"));
|
|
3723
|
+
}
|
|
3724
|
+
function printVerboseProcedurePlan(items) {
|
|
3725
|
+
for (const item of items) {
|
|
3726
|
+
switch (item.action) {
|
|
3727
|
+
case "invalid":
|
|
3728
|
+
clack3.log.error(`[invalid] ${item.filePath}: ${item.error}`);
|
|
3729
|
+
break;
|
|
3730
|
+
case "create":
|
|
3731
|
+
clack3.log.step(`[create] ${item.candidate.procedure.procedure_key}: ${item.candidate.filePath}`);
|
|
3732
|
+
break;
|
|
3733
|
+
case "update_source_only":
|
|
3734
|
+
clack3.log.step(`[update_source_only] ${item.candidate.procedure.procedure_key}: ${item.candidate.filePath} -> reuse ${item.existing.id}`);
|
|
3735
|
+
break;
|
|
3736
|
+
case "supersede":
|
|
3737
|
+
clack3.log.step(`[supersede] ${item.candidate.procedure.procedure_key}: ${item.existing.id} -> new revision`);
|
|
3738
|
+
break;
|
|
3739
|
+
case "unchanged":
|
|
3740
|
+
clack3.log.step(`[unchanged] ${item.candidate.procedure.procedure_key}: ${item.candidate.filePath}`);
|
|
3741
|
+
break;
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
function printVerboseProcedureExecution(items) {
|
|
3746
|
+
for (const item of items) {
|
|
3747
|
+
switch (item.action) {
|
|
3748
|
+
case "created":
|
|
3749
|
+
clack3.log.step(`[created] ${item.procedureKey}: ${item.procedureId}`);
|
|
3750
|
+
break;
|
|
3751
|
+
case "updated_source_only":
|
|
3752
|
+
clack3.log.step(`[updated_source_only] ${item.procedureKey}: ${item.procedureId}`);
|
|
3753
|
+
break;
|
|
3754
|
+
case "superseded":
|
|
3755
|
+
clack3.log.step(`[superseded] ${item.procedureKey}: ${item.previousProcedureId} -> ${item.procedureId}`);
|
|
3756
|
+
break;
|
|
3757
|
+
case "unchanged":
|
|
3758
|
+
clack3.log.step(`[unchanged] ${item.procedureKey}: ${item.procedureId}`);
|
|
3759
|
+
break;
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
function countPlannedWrites(items) {
|
|
3764
|
+
return items.filter((item) => item.action === "create" || item.action === "update_source_only" || item.action === "supersede").length;
|
|
3765
|
+
}
|
|
3766
|
+
function formatProcedurePlanTail(plan) {
|
|
3767
|
+
return [
|
|
3768
|
+
`${plan.totals.create} create`,
|
|
3769
|
+
`${plan.totals.updateSourceOnly} source update`,
|
|
3770
|
+
`${plan.totals.supersede} supersede`,
|
|
3771
|
+
`${plan.totals.unchanged} unchanged`,
|
|
3772
|
+
`${plan.totals.invalid} invalid`
|
|
3773
|
+
].join(", ");
|
|
3774
|
+
}
|
|
3775
|
+
function formatProcedureExecutionTail(execution) {
|
|
3776
|
+
return [
|
|
3777
|
+
`${execution.totals.created} created`,
|
|
3778
|
+
`${execution.totals.updatedSourceOnly} source updated`,
|
|
3779
|
+
`${execution.totals.superseded} superseded`,
|
|
3780
|
+
`${execution.totals.unchanged} unchanged`
|
|
3781
|
+
].join(", ");
|
|
3782
|
+
}
|
|
3783
|
+
function pluralize2(value, singular, plural) {
|
|
3784
|
+
return value === 1 ? singular : plural ?? `${singular}s`;
|
|
3785
|
+
}
|
|
3786
|
+
function formatUnknownError4(error) {
|
|
3787
|
+
return error instanceof Error ? error.message : String(error);
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3288
3790
|
// src/cli/commands/ingest.ts
|
|
3289
3791
|
var MIN_INGEST_CONCURRENCY = 1;
|
|
3290
3792
|
var MAX_INGEST_CONCURRENCY = 50;
|
|
@@ -3292,6 +3794,7 @@ function registerIngestCommand(program2) {
|
|
|
3292
3794
|
const ingestCommand = program2.command("ingest").description("Ingest OpenClaw transcripts and derived memory artifacts");
|
|
3293
3795
|
registerIngestEntriesCommand(ingestCommand);
|
|
3294
3796
|
registerIngestEpisodesCommand(ingestCommand);
|
|
3797
|
+
registerIngestProceduresCommand(ingestCommand);
|
|
3295
3798
|
}
|
|
3296
3799
|
function registerIngestEntriesCommand(parent) {
|
|
3297
3800
|
const ingestCommand = parent.command("entries <path>", { isDefault: true }).description("Ingest OpenClaw session files into the knowledge database").option("--verbose", "Show detailed progress").option("--dry-run", "Parse and extract without storing").addOption(new Option2("--whole-file <mode>", "Whole-file mode: auto|force|never").choices(["auto", "force", "never"]).default("auto")).option("--skip-dedup", "Skip within-batch semantic dedup").addOption(new Option2("--concurrency <n>", "Max files to extract in parallel").argParser(parseConcurrency));
|
|
@@ -3300,7 +3803,7 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3300
3803
|
let db = null;
|
|
3301
3804
|
const commandInput = normalizeIngestEntriesCommand(targetPath, options);
|
|
3302
3805
|
setVerbose(commandInput.verbose);
|
|
3303
|
-
|
|
3806
|
+
clack4.intro(banner());
|
|
3304
3807
|
try {
|
|
3305
3808
|
const config = readConfig();
|
|
3306
3809
|
const dbPath = config.dbPath;
|
|
@@ -3319,20 +3822,20 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3319
3822
|
const claimApiKey = claimModel ? resolveLlmApiKey(config, claimModel.provider) : void 0;
|
|
3320
3823
|
const sharedEmbedding = createEmbeddingClient(resolveEmbeddingApiKey(config), resolveEmbeddingModel(config));
|
|
3321
3824
|
if (commandInput.verbose) {
|
|
3322
|
-
|
|
3825
|
+
clack4.log.step(`Discovering transcript files in ${path10.resolve(commandInput.targetPath)}...`);
|
|
3323
3826
|
}
|
|
3324
3827
|
const files = await localTranscriptFiles.discoverFiles(commandInput.targetPath);
|
|
3325
3828
|
if (files.length === 0) {
|
|
3326
|
-
|
|
3327
|
-
|
|
3829
|
+
clack4.log.warn(`No transcript files found at ${path10.resolve(commandInput.targetPath)}.`);
|
|
3830
|
+
clack4.outro("Nothing to ingest.");
|
|
3328
3831
|
return;
|
|
3329
3832
|
}
|
|
3330
|
-
|
|
3833
|
+
clack4.log.info(
|
|
3331
3834
|
[
|
|
3332
3835
|
formatLabel("Extraction model", `${provider}/${modelId}`),
|
|
3333
3836
|
formatLabel("Dedup model", commandInput.skipDedup ? "skipped" : `${dedupProvider}/${dedupModelId}`),
|
|
3334
3837
|
formatLabel("Database", dbPath),
|
|
3335
|
-
formatLabel("Files", `${files.length} ${
|
|
3838
|
+
formatLabel("Files", `${files.length} ${pluralize3(files.length, "file")} found`),
|
|
3336
3839
|
formatLabel("Whole-file", commandInput.wholeFile),
|
|
3337
3840
|
formatLabel("Within-batch dedup", commandInput.skipDedup ? "skipped" : "enabled"),
|
|
3338
3841
|
formatLabel("Embeddings", "stored"),
|
|
@@ -3340,11 +3843,11 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3340
3843
|
].join("\n")
|
|
3341
3844
|
);
|
|
3342
3845
|
if (commandInput.dryRun) {
|
|
3343
|
-
|
|
3846
|
+
clack4.log.warn("Dry run mode - no entries will be stored.");
|
|
3344
3847
|
}
|
|
3345
3848
|
const useVerboseBulkWriteProgress = commandInput.verbose && !commandInput.dryRun;
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3849
|
+
const spinner6 = useVerboseBulkWriteProgress ? null : clack4.spinner();
|
|
3850
|
+
spinner6?.start(`Processing transcripts... (0/${files.length} extracted)`);
|
|
3348
3851
|
const ingestResult = await ingestDiscoveredFiles(
|
|
3349
3852
|
files,
|
|
3350
3853
|
{
|
|
@@ -3367,23 +3870,23 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3367
3870
|
skipDedup: commandInput.skipDedup,
|
|
3368
3871
|
extractionContext: config.extractionContext,
|
|
3369
3872
|
onExtractionProgress: (completed, total) => {
|
|
3370
|
-
|
|
3873
|
+
spinner6?.message(`Processing transcripts... (${completed}/${total} extracted)`);
|
|
3371
3874
|
},
|
|
3372
3875
|
onStageProgress: (event) => {
|
|
3373
|
-
|
|
3876
|
+
spinner6?.message(progressMessageForIngestStage(event));
|
|
3374
3877
|
},
|
|
3375
3878
|
onDedupProgress: (event) => {
|
|
3376
|
-
|
|
3879
|
+
spinner6?.message(progressMessageForDedup(event));
|
|
3377
3880
|
},
|
|
3378
3881
|
onClaimExtractionProgress: (event) => {
|
|
3379
|
-
|
|
3882
|
+
spinner6?.message(progressMessageForClaimExtraction(event));
|
|
3380
3883
|
},
|
|
3381
3884
|
onBulkWriteProgress: useVerboseBulkWriteProgress ? reportBulkWriteProgress : (event) => {
|
|
3382
|
-
|
|
3885
|
+
spinner6?.message(progressMessageForBulkWrite(event.phase));
|
|
3383
3886
|
}
|
|
3384
3887
|
}
|
|
3385
3888
|
);
|
|
3386
|
-
|
|
3889
|
+
spinner6?.stop("Ingest pipeline complete.");
|
|
3387
3890
|
const extractionRuns = ingestResult.extractionRuns;
|
|
3388
3891
|
const extractedResults = extractionRuns.map(({ result }) => result);
|
|
3389
3892
|
const extractedSuccesses = extractedResults.filter((result) => result.skipped !== true && result.error === void 0);
|
|
@@ -3396,7 +3899,7 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3396
3899
|
printDedupSummary(dedupResult, taggedEntries, commandInput, dedupUsage.totalCost);
|
|
3397
3900
|
}
|
|
3398
3901
|
if (claimKeyHealth) {
|
|
3399
|
-
|
|
3902
|
+
clack4.log.info(formatClaimKeyHealthSummary(claimKeyHealth));
|
|
3400
3903
|
}
|
|
3401
3904
|
const totals = {
|
|
3402
3905
|
stored: 0,
|
|
@@ -3419,9 +3922,9 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3419
3922
|
totals.skippedFiles += 1;
|
|
3420
3923
|
if (commandInput.verbose) {
|
|
3421
3924
|
printVerboseFileDetails(result, commandInput, usage);
|
|
3422
|
-
|
|
3925
|
+
clack4.log.step(buildSkippedMessage(path10.basename(result.file)));
|
|
3423
3926
|
} else {
|
|
3424
|
-
|
|
3927
|
+
clack4.log.step(buildSkippedMessage(path10.basename(result.file)));
|
|
3425
3928
|
}
|
|
3426
3929
|
continue;
|
|
3427
3930
|
}
|
|
@@ -3430,7 +3933,7 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3430
3933
|
if (commandInput.verbose) {
|
|
3431
3934
|
printVerboseFileDetails(result, commandInput, usage);
|
|
3432
3935
|
}
|
|
3433
|
-
|
|
3936
|
+
clack4.log.error(buildFailureMessage(path10.basename(result.file), result, commandInput, usage, index === 0));
|
|
3434
3937
|
continue;
|
|
3435
3938
|
}
|
|
3436
3939
|
const storeResult = result.storeResult ?? emptyStoreResult2();
|
|
@@ -3440,23 +3943,23 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3440
3943
|
if (commandInput.verbose) {
|
|
3441
3944
|
printVerboseFileDetails(result, commandInput, usage);
|
|
3442
3945
|
}
|
|
3443
|
-
|
|
3946
|
+
clack4.log.step(buildSuccessMessage(path10.basename(result.file), result, commandInput, usage, index === 0));
|
|
3444
3947
|
}
|
|
3445
|
-
const summaryParts = [`${totals.stored} ${
|
|
3948
|
+
const summaryParts = [`${totals.stored} ${pluralize3(totals.stored, "entry", "entries")} stored`, `${totals.deduped} deduped`];
|
|
3446
3949
|
if (totals.rejected > 0) {
|
|
3447
3950
|
summaryParts.push(`${totals.rejected} rejected`);
|
|
3448
3951
|
}
|
|
3449
3952
|
if (totals.skippedFiles > 0) {
|
|
3450
|
-
summaryParts.push(`${totals.skippedFiles} ${
|
|
3953
|
+
summaryParts.push(`${totals.skippedFiles} ${pluralize3(totals.skippedFiles, "file")} skipped`);
|
|
3451
3954
|
}
|
|
3452
3955
|
if (totals.failedFiles > 0) {
|
|
3453
|
-
summaryParts.push(`${totals.failedFiles} ${
|
|
3956
|
+
summaryParts.push(`${totals.failedFiles} ${pluralize3(totals.failedFiles, "file")} failed`);
|
|
3454
3957
|
}
|
|
3455
3958
|
if (totals.warnings > 0) {
|
|
3456
|
-
summaryParts.push(`${totals.warnings} ${
|
|
3959
|
+
summaryParts.push(`${totals.warnings} ${pluralize3(totals.warnings, "warning")}`);
|
|
3457
3960
|
}
|
|
3458
3961
|
if (usageTotals.calls > 0) {
|
|
3459
|
-
|
|
3962
|
+
clack4.log.info(
|
|
3460
3963
|
[
|
|
3461
3964
|
formatLabel(
|
|
3462
3965
|
"Tokens",
|
|
@@ -3468,11 +3971,11 @@ function registerIngestEntriesCommand(parent) {
|
|
|
3468
3971
|
);
|
|
3469
3972
|
}
|
|
3470
3973
|
const dryRunSuffix = commandInput.dryRun ? " Dry run only." : "";
|
|
3471
|
-
|
|
3974
|
+
clack4.outro(`Done: ${summaryParts.join(", ")}. (${formatCost2(usageTotals.totalCost)}, ${formatDurationMs2(Date.now() - startedAt)})${dryRunSuffix}`);
|
|
3472
3975
|
} catch (error) {
|
|
3473
3976
|
process.exitCode = 1;
|
|
3474
|
-
|
|
3475
|
-
|
|
3977
|
+
clack4.log.error(formatUnknownError5(error));
|
|
3978
|
+
clack4.outro(ui.error("Ingest failed"));
|
|
3476
3979
|
} finally {
|
|
3477
3980
|
await db?.close();
|
|
3478
3981
|
}
|
|
@@ -3544,15 +4047,15 @@ function printVerboseFileDetails(result, options, usage) {
|
|
|
3544
4047
|
}
|
|
3545
4048
|
const fileLabel = result.file;
|
|
3546
4049
|
if (result.skipped) {
|
|
3547
|
-
|
|
4050
|
+
clack4.log.step(`${fileLabel}: skipped because the ingest hash matched.`);
|
|
3548
4051
|
return;
|
|
3549
4052
|
}
|
|
3550
4053
|
if (result.error) {
|
|
3551
4054
|
for (const chunkDetail of result.chunkDetails) {
|
|
3552
|
-
|
|
4055
|
+
clack4.log.step(formatChunkDetail(result.file, chunkDetail));
|
|
3553
4056
|
}
|
|
3554
4057
|
const lines2 = [
|
|
3555
|
-
`${fileLabel}: ${result.messageCount} ${
|
|
4058
|
+
`${fileLabel}: ${result.messageCount} ${pluralize3(result.messageCount, "message")} parsed before failure`,
|
|
3556
4059
|
`${fileLabel}: extraction ${result.successfulChunks}/${result.chunkCount} chunks succeeded`
|
|
3557
4060
|
];
|
|
3558
4061
|
const usageLine2 = formatVerboseUsageLine(fileLabel, usage);
|
|
@@ -3560,19 +4063,19 @@ function printVerboseFileDetails(result, options, usage) {
|
|
|
3560
4063
|
lines2.push(usageLine2);
|
|
3561
4064
|
}
|
|
3562
4065
|
lines2.push(`${fileLabel}: duration ${formatDurationMs2(result.durationMs)}`);
|
|
3563
|
-
|
|
4066
|
+
clack4.log.step(lines2.join("\n"));
|
|
3564
4067
|
for (const warning of result.warnings) {
|
|
3565
4068
|
if (warning !== result.error) {
|
|
3566
|
-
|
|
4069
|
+
clack4.log.warn(`${fileLabel}: ${warning}`);
|
|
3567
4070
|
}
|
|
3568
4071
|
}
|
|
3569
4072
|
return;
|
|
3570
4073
|
}
|
|
3571
4074
|
for (const chunkDetail of result.chunkDetails) {
|
|
3572
|
-
|
|
4075
|
+
clack4.log.step(formatChunkDetail(result.file, chunkDetail));
|
|
3573
4076
|
}
|
|
3574
4077
|
const lines = [
|
|
3575
|
-
`${fileLabel}: ${result.messageCount} ${
|
|
4078
|
+
`${fileLabel}: ${result.messageCount} ${pluralize3(result.messageCount, "message")} parsed`,
|
|
3576
4079
|
`${fileLabel}: extraction ${result.successfulChunks}/${result.chunkCount} chunks succeeded`,
|
|
3577
4080
|
`${fileLabel}: store ${formatStoreSummary(result)}`
|
|
3578
4081
|
];
|
|
@@ -3581,15 +4084,15 @@ function printVerboseFileDetails(result, options, usage) {
|
|
|
3581
4084
|
lines.push(usageLine);
|
|
3582
4085
|
}
|
|
3583
4086
|
lines.push(`${fileLabel}: duration ${formatDurationMs2(result.durationMs)}`);
|
|
3584
|
-
|
|
4087
|
+
clack4.log.step(lines.join("\n"));
|
|
3585
4088
|
for (const warning of result.warnings) {
|
|
3586
|
-
|
|
4089
|
+
clack4.log.warn(`${fileLabel}: ${warning}`);
|
|
3587
4090
|
}
|
|
3588
4091
|
}
|
|
3589
4092
|
function buildSuccessMessage(fileLabel, result, options, usage, isFirstFile) {
|
|
3590
4093
|
const storeResult = result.storeResult ?? emptyStoreResult2();
|
|
3591
4094
|
const details = [
|
|
3592
|
-
`${result.messageCount} ${
|
|
4095
|
+
`${result.messageCount} ${pluralize3(result.messageCount, "message")}`,
|
|
3593
4096
|
`${result.entriesExtracted} extracted`,
|
|
3594
4097
|
`${storeResult.stored} stored`
|
|
3595
4098
|
];
|
|
@@ -3600,7 +4103,7 @@ function buildSuccessMessage(fileLabel, result, options, usage, isFirstFile) {
|
|
|
3600
4103
|
details.push(`${storeResult.rejected} rejected`);
|
|
3601
4104
|
}
|
|
3602
4105
|
if (result.failedChunks > 0) {
|
|
3603
|
-
details.push(`${result.failedChunks} chunk ${
|
|
4106
|
+
details.push(`${result.failedChunks} chunk ${pluralize3(result.failedChunks, "failure")}`);
|
|
3604
4107
|
}
|
|
3605
4108
|
if (options.dryRun === true) {
|
|
3606
4109
|
details.push("dry run");
|
|
@@ -3625,7 +4128,7 @@ function formatStoreSummary(result) {
|
|
|
3625
4128
|
const storeResult = result.storeResult ?? emptyStoreResult2();
|
|
3626
4129
|
const parts = [`${storeResult.stored} stored`, `${storeResult.skipped} deduped`, `${storeResult.rejected} rejected`];
|
|
3627
4130
|
if (result.failedChunks > 0) {
|
|
3628
|
-
parts.push(`${result.failedChunks} chunk ${
|
|
4131
|
+
parts.push(`${result.failedChunks} chunk ${pluralize3(result.failedChunks, "failure")}`);
|
|
3629
4132
|
}
|
|
3630
4133
|
return parts.join(", ");
|
|
3631
4134
|
}
|
|
@@ -3652,12 +4155,12 @@ function formatVerboseUsageLine(fileLabel, usage) {
|
|
|
3652
4155
|
if (usage.fileCost === 0 && usage.fileCalls === 0) {
|
|
3653
4156
|
return void 0;
|
|
3654
4157
|
}
|
|
3655
|
-
return `${fileLabel}: cost ${formatCost2(usage.fileCost)} (${usage.fileCalls} ${
|
|
4158
|
+
return `${fileLabel}: cost ${formatCost2(usage.fileCost)} (${usage.fileCalls} ${pluralize3(usage.fileCalls, "LLM call")})`;
|
|
3656
4159
|
}
|
|
3657
|
-
function
|
|
4160
|
+
function formatUnknownError5(error) {
|
|
3658
4161
|
return error instanceof Error ? error.message : String(error);
|
|
3659
4162
|
}
|
|
3660
|
-
function
|
|
4163
|
+
function pluralize3(value, singular, plural) {
|
|
3661
4164
|
return value === 1 ? singular : plural ?? `${singular}s`;
|
|
3662
4165
|
}
|
|
3663
4166
|
function getDisplayResult(result, storeResults) {
|
|
@@ -3683,33 +4186,33 @@ function collectTaggedEntries2(results) {
|
|
|
3683
4186
|
}
|
|
3684
4187
|
function printDedupSummary(dedupResult, taggedEntries, options, dedupCost) {
|
|
3685
4188
|
if (taggedEntries.length === 0) {
|
|
3686
|
-
|
|
4189
|
+
clack4.log.step("Dedup: 0 entries extracted, nothing to arbitrate.");
|
|
3687
4190
|
return;
|
|
3688
4191
|
}
|
|
3689
4192
|
if (options.skipDedup === true) {
|
|
3690
|
-
|
|
4193
|
+
clack4.log.step(`Dedup: skipped (--skip-dedup), ${taggedEntries.length} ${pluralize3(taggedEntries.length, "entry", "entries")} passed through.`);
|
|
3691
4194
|
return;
|
|
3692
4195
|
}
|
|
3693
|
-
|
|
3694
|
-
`Dedup: ${dedupResult.inputCount} ${
|
|
4196
|
+
clack4.log.step(
|
|
4197
|
+
`Dedup: ${dedupResult.inputCount} ${pluralize3(dedupResult.inputCount, "entry", "entries")} -> ${dedupResult.clustersArbitrated} similar ${pluralize3(dedupResult.clustersArbitrated, "cluster")} found (similarity > ${formatThreshold(dedupResult.similarityThreshold)})`
|
|
3695
4198
|
);
|
|
3696
|
-
|
|
3697
|
-
`Dedup: ${dedupResult.clustersArbitrated} ${
|
|
4199
|
+
clack4.log.step(
|
|
4200
|
+
`Dedup: ${dedupResult.clustersArbitrated} ${pluralize3(dedupResult.clustersArbitrated, "cluster")} arbitrated (${dedupResult.llmCalls} ${pluralize3(dedupResult.llmCalls, "LLM call")})`
|
|
3698
4201
|
);
|
|
3699
|
-
|
|
3700
|
-
`Dedup: ${dedupResult.survivors.length} ${
|
|
4202
|
+
clack4.log.step(
|
|
4203
|
+
`Dedup: ${dedupResult.survivors.length} ${pluralize3(dedupResult.survivors.length, "entry", "entries")} survived, ${dedupResult.removedCount} removed (${formatCost2(dedupCost)})`
|
|
3701
4204
|
);
|
|
3702
4205
|
for (const warning of dedupResult.warnings) {
|
|
3703
|
-
|
|
4206
|
+
clack4.log.warn(`Dedup: ${warning}`);
|
|
3704
4207
|
}
|
|
3705
4208
|
if (options.verbose !== true) {
|
|
3706
4209
|
return;
|
|
3707
4210
|
}
|
|
3708
4211
|
for (const [clusterIndex, detail] of dedupResult.clusterDetails.entries()) {
|
|
3709
|
-
|
|
4212
|
+
clack4.log.step(formatDedupClusterDetail(clusterIndex, detail, taggedEntries));
|
|
3710
4213
|
}
|
|
3711
|
-
|
|
3712
|
-
`Dedup: ${dedupResult.singletonsPassedThrough} ${
|
|
4214
|
+
clack4.log.step(
|
|
4215
|
+
`Dedup: ${dedupResult.singletonsPassedThrough} ${pluralize3(dedupResult.singletonsPassedThrough, "singleton")} passed through (no similar neighbors)`
|
|
3713
4216
|
);
|
|
3714
4217
|
}
|
|
3715
4218
|
function formatDedupClusterDetail(clusterIndex, detail, taggedEntries) {
|
|
@@ -3718,7 +4221,7 @@ function formatDedupClusterDetail(clusterIndex, detail, taggedEntries) {
|
|
|
3718
4221
|
localIndexByOriginal.set(entryIndex, localIndex);
|
|
3719
4222
|
});
|
|
3720
4223
|
const lines = [
|
|
3721
|
-
`Dedup cluster ${clusterIndex + 1} (${detail.entryIndices.length} ${
|
|
4224
|
+
`Dedup cluster ${clusterIndex + 1} (${detail.entryIndices.length} ${pluralize3(detail.entryIndices.length, "entry", "entries")}, max similarity ${detail.maxSimilarity.toFixed(2)}):`
|
|
3722
4225
|
];
|
|
3723
4226
|
for (const [localIndex, originalIndex] of detail.entryIndices.entries()) {
|
|
3724
4227
|
const taggedEntry = taggedEntries.find((entry2) => entry2.originalIndex === originalIndex);
|
|
@@ -3778,11 +4281,11 @@ function progressMessageForIngestStage(event) {
|
|
|
3778
4281
|
case "claim_extraction_start":
|
|
3779
4282
|
return "Extracting claim keys...";
|
|
3780
4283
|
case "store_start":
|
|
3781
|
-
return `Running store pipeline for ${event.totalEntries} ${
|
|
4284
|
+
return `Running store pipeline for ${event.totalEntries} ${pluralize3(event.totalEntries, "entry", "entries")}...`;
|
|
3782
4285
|
}
|
|
3783
4286
|
}
|
|
3784
4287
|
function progressMessageForDedup(event) {
|
|
3785
|
-
return `Deduplicating entries... ${event.completedClusters}/${event.totalClusters} ${
|
|
4288
|
+
return `Deduplicating entries... ${event.completedClusters}/${event.totalClusters} ${pluralize3(event.totalClusters, "cluster")} arbitrated (${event.completedEntries}/${event.totalEntries} entries covered)`;
|
|
3786
4289
|
}
|
|
3787
4290
|
function progressMessageForClaimExtraction(event) {
|
|
3788
4291
|
switch (event.phase) {
|
|
@@ -3807,16 +4310,16 @@ function progressMessageForBulkWrite(phase) {
|
|
|
3807
4310
|
function reportBulkWriteProgress(event) {
|
|
3808
4311
|
switch (event.phase) {
|
|
3809
4312
|
case "prepare_start":
|
|
3810
|
-
|
|
4313
|
+
clack4.log.step("Store: dropping FTS triggers and vector index for bulk writes...");
|
|
3811
4314
|
break;
|
|
3812
4315
|
case "store_complete":
|
|
3813
|
-
|
|
4316
|
+
clack4.log.step("Store phase complete.");
|
|
3814
4317
|
break;
|
|
3815
4318
|
case "finalize_start":
|
|
3816
|
-
|
|
4319
|
+
clack4.log.step("Store: rebuilding FTS and vector index...");
|
|
3817
4320
|
break;
|
|
3818
4321
|
case "finalize_complete":
|
|
3819
|
-
|
|
4322
|
+
clack4.log.step(`Store: indexes rebuilt (${formatDurationMs2(event.durationMs ?? 0)}).`);
|
|
3820
4323
|
break;
|
|
3821
4324
|
}
|
|
3822
4325
|
}
|
|
@@ -3870,33 +4373,33 @@ import { getModels } from "@mariozechner/pi-ai";
|
|
|
3870
4373
|
|
|
3871
4374
|
// src/cli/ui.ts
|
|
3872
4375
|
import os2 from "os";
|
|
3873
|
-
import
|
|
3874
|
-
import * as
|
|
4376
|
+
import path11 from "path";
|
|
4377
|
+
import * as clack5 from "@clack/prompts";
|
|
3875
4378
|
function createCliPrompts() {
|
|
3876
4379
|
return {
|
|
3877
|
-
intro: (message) =>
|
|
3878
|
-
outro: (message) =>
|
|
3879
|
-
note: (message, title) =>
|
|
3880
|
-
cancel: (message) =>
|
|
3881
|
-
isCancel:
|
|
4380
|
+
intro: (message) => clack5.intro(message),
|
|
4381
|
+
outro: (message) => clack5.outro(message),
|
|
4382
|
+
note: (message, title) => clack5.note(message, title),
|
|
4383
|
+
cancel: (message) => clack5.cancel(message),
|
|
4384
|
+
isCancel: clack5.isCancel,
|
|
3882
4385
|
select: async (options) => {
|
|
3883
|
-
return await
|
|
4386
|
+
return await clack5.select({
|
|
3884
4387
|
message: options.message,
|
|
3885
4388
|
options: options.options,
|
|
3886
4389
|
...options.initialValue !== void 0 ? { initialValue: options.initialValue } : {}
|
|
3887
4390
|
});
|
|
3888
4391
|
},
|
|
3889
4392
|
confirm: async (options) => {
|
|
3890
|
-
return await
|
|
4393
|
+
return await clack5.confirm(options);
|
|
3891
4394
|
},
|
|
3892
4395
|
password: async (options) => {
|
|
3893
|
-
return await
|
|
4396
|
+
return await clack5.password(options);
|
|
3894
4397
|
},
|
|
3895
4398
|
text: async (options) => {
|
|
3896
|
-
return await
|
|
4399
|
+
return await clack5.text(options);
|
|
3897
4400
|
},
|
|
3898
|
-
spinner: () =>
|
|
3899
|
-
log:
|
|
4401
|
+
spinner: () => clack5.spinner(),
|
|
4402
|
+
log: clack5.log
|
|
3900
4403
|
};
|
|
3901
4404
|
}
|
|
3902
4405
|
var cliPrompts = createCliPrompts();
|
|
@@ -3909,12 +4412,12 @@ function resolveUserPath2(value) {
|
|
|
3909
4412
|
return os2.homedir();
|
|
3910
4413
|
}
|
|
3911
4414
|
if (trimmed.startsWith("~/")) {
|
|
3912
|
-
return
|
|
4415
|
+
return path11.join(os2.homedir(), trimmed.slice(2));
|
|
3913
4416
|
}
|
|
3914
4417
|
if (trimmed.startsWith("~\\")) {
|
|
3915
|
-
return
|
|
4418
|
+
return path11.join(os2.homedir(), trimmed.slice(2));
|
|
3916
4419
|
}
|
|
3917
|
-
return
|
|
4420
|
+
return path11.resolve(trimmed);
|
|
3918
4421
|
}
|
|
3919
4422
|
function formatPathForDisplay(filePath) {
|
|
3920
4423
|
const home = os2.homedir();
|
|
@@ -3999,7 +4502,7 @@ async function withTimeout(promise, timeoutMs, message) {
|
|
|
3999
4502
|
});
|
|
4000
4503
|
return await Promise.race([promise, timeout]);
|
|
4001
4504
|
}
|
|
4002
|
-
function
|
|
4505
|
+
function formatUnknownError6(error) {
|
|
4003
4506
|
return error instanceof Error ? error.message : String(error);
|
|
4004
4507
|
}
|
|
4005
4508
|
|
|
@@ -4285,7 +4788,7 @@ function getSetupReadiness(config, env = process.env) {
|
|
|
4285
4788
|
} catch (error) {
|
|
4286
4789
|
return {
|
|
4287
4790
|
ready: false,
|
|
4288
|
-
guidance:
|
|
4791
|
+
guidance: formatUnknownError6(error)
|
|
4289
4792
|
};
|
|
4290
4793
|
}
|
|
4291
4794
|
}
|
|
@@ -4629,14 +5132,14 @@ async function selectAuthModel(prompts, runtime, auth, existingModel) {
|
|
|
4629
5132
|
async function verifyManualPrimaryCredential(prompts, runtime, auth, provider, initialApiKey, probeModel) {
|
|
4630
5133
|
let apiKey = initialApiKey;
|
|
4631
5134
|
while (true) {
|
|
4632
|
-
const
|
|
4633
|
-
|
|
5135
|
+
const spinner6 = prompts.spinner();
|
|
5136
|
+
spinner6.start(`Testing ${describeAuthMethod(auth)}...`);
|
|
4634
5137
|
const result = await runtime.testLlmConnection(provider, probeModel, apiKey);
|
|
4635
5138
|
if (result.ok) {
|
|
4636
|
-
|
|
5139
|
+
spinner6.stop(ui.success("Connection verified"));
|
|
4637
5140
|
return { apiKey, verified: true };
|
|
4638
5141
|
}
|
|
4639
|
-
|
|
5142
|
+
spinner6.stop(ui.error(`Connection failed: ${result.error ?? "unknown error"}`));
|
|
4640
5143
|
const action = await prompts.select({
|
|
4641
5144
|
message: "The provider connection test failed. What do you want to do?",
|
|
4642
5145
|
options: [
|
|
@@ -4667,14 +5170,14 @@ async function verifyManualPrimaryCredential(prompts, runtime, auth, provider, i
|
|
|
4667
5170
|
}
|
|
4668
5171
|
async function verifyDetectedPrimaryCredential(prompts, runtime, auth, provider, apiKey, probeModel) {
|
|
4669
5172
|
while (true) {
|
|
4670
|
-
const
|
|
4671
|
-
|
|
5173
|
+
const spinner6 = prompts.spinner();
|
|
5174
|
+
spinner6.start(`Testing ${describeAuthMethod(auth)}...`);
|
|
4672
5175
|
const result = await runtime.testLlmConnection(provider, probeModel, apiKey);
|
|
4673
5176
|
if (result.ok) {
|
|
4674
|
-
|
|
5177
|
+
spinner6.stop(ui.success("Connection verified"));
|
|
4675
5178
|
return { apiKey, verified: true };
|
|
4676
5179
|
}
|
|
4677
|
-
|
|
5180
|
+
spinner6.stop(ui.error(`Connection failed: ${result.error ?? "unknown error"}`));
|
|
4678
5181
|
const action = await prompts.select({
|
|
4679
5182
|
message: "The provider connection test failed. What do you want to do?",
|
|
4680
5183
|
options: [
|
|
@@ -4693,14 +5196,14 @@ async function verifyDetectedPrimaryCredential(prompts, runtime, auth, provider,
|
|
|
4693
5196
|
}
|
|
4694
5197
|
async function verifySharedEmbeddingKey(prompts, runtime, apiKey) {
|
|
4695
5198
|
while (true) {
|
|
4696
|
-
const
|
|
4697
|
-
|
|
5199
|
+
const spinner6 = prompts.spinner();
|
|
5200
|
+
spinner6.start(`Testing embeddings with OpenAI ${EMBEDDING_MODEL}...`);
|
|
4698
5201
|
const result = await runtime.testEmbeddingConnection(apiKey, EMBEDDING_MODEL);
|
|
4699
5202
|
if (result.ok) {
|
|
4700
|
-
|
|
5203
|
+
spinner6.stop(ui.success("Embeddings verified"));
|
|
4701
5204
|
return { verified: true };
|
|
4702
5205
|
}
|
|
4703
|
-
|
|
5206
|
+
spinner6.stop(ui.error(`Embeddings test failed: ${result.error ?? "unknown error"}`));
|
|
4704
5207
|
const action = await prompts.select({
|
|
4705
5208
|
message: "The embeddings test failed. What do you want to do?",
|
|
4706
5209
|
options: [
|
|
@@ -4720,14 +5223,14 @@ async function verifySharedEmbeddingKey(prompts, runtime, apiKey) {
|
|
|
4720
5223
|
async function verifySeparateEmbeddingKey(prompts, runtime, initialApiKey) {
|
|
4721
5224
|
let apiKey = initialApiKey;
|
|
4722
5225
|
while (true) {
|
|
4723
|
-
const
|
|
4724
|
-
|
|
5226
|
+
const spinner6 = prompts.spinner();
|
|
5227
|
+
spinner6.start(`Testing embeddings with OpenAI ${EMBEDDING_MODEL}...`);
|
|
4725
5228
|
const result = await runtime.testEmbeddingConnection(apiKey, EMBEDDING_MODEL);
|
|
4726
5229
|
if (result.ok) {
|
|
4727
|
-
|
|
5230
|
+
spinner6.stop(ui.success("Embeddings verified"));
|
|
4728
5231
|
return { apiKey, verified: true };
|
|
4729
5232
|
}
|
|
4730
|
-
|
|
5233
|
+
spinner6.stop(ui.error(`Embeddings test failed: ${result.error ?? "unknown error"}`));
|
|
4731
5234
|
const action = await prompts.select({
|
|
4732
5235
|
message: "The embeddings test failed. What do you want to do?",
|
|
4733
5236
|
options: [
|
|
@@ -4865,7 +5368,7 @@ var defaultSetupRuntime = {
|
|
|
4865
5368
|
} catch (error) {
|
|
4866
5369
|
return {
|
|
4867
5370
|
ok: false,
|
|
4868
|
-
error:
|
|
5371
|
+
error: formatUnknownError6(error)
|
|
4869
5372
|
};
|
|
4870
5373
|
}
|
|
4871
5374
|
},
|
|
@@ -4880,7 +5383,7 @@ var defaultSetupRuntime = {
|
|
|
4880
5383
|
} catch (error) {
|
|
4881
5384
|
return {
|
|
4882
5385
|
ok: false,
|
|
4883
|
-
error:
|
|
5386
|
+
error: formatUnknownError6(error)
|
|
4884
5387
|
};
|
|
4885
5388
|
}
|
|
4886
5389
|
},
|
|
@@ -4935,7 +5438,7 @@ async function runSetupCommand() {
|
|
|
4935
5438
|
prompts.outro(`Next: ${ui.bold('agenr recall "test"')} or ${ui.bold("agenr ingest <path>")}`);
|
|
4936
5439
|
} catch (error) {
|
|
4937
5440
|
process.exitCode = 1;
|
|
4938
|
-
prompts.log.error(
|
|
5441
|
+
prompts.log.error(formatUnknownError6(error));
|
|
4939
5442
|
prompts.outro(ui.error("Setup failed"));
|
|
4940
5443
|
}
|
|
4941
5444
|
}
|
|
@@ -4987,8 +5490,8 @@ function formatTokenCount(tokens) {
|
|
|
4987
5490
|
|
|
4988
5491
|
// src/cli/commands/init/external-commands.ts
|
|
4989
5492
|
import { execFile, execFileSync } from "child_process";
|
|
4990
|
-
import
|
|
4991
|
-
import
|
|
5493
|
+
import fs7 from "fs/promises";
|
|
5494
|
+
import path12 from "path";
|
|
4992
5495
|
var OPENCLAW_PLUGIN_PACKAGE = "@agenr/agenr-plugin";
|
|
4993
5496
|
function execAsync(command, args, options) {
|
|
4994
5497
|
return new Promise((resolve, reject) => {
|
|
@@ -5085,10 +5588,10 @@ async function restartOpenClawGateway() {
|
|
|
5085
5588
|
}
|
|
5086
5589
|
}
|
|
5087
5590
|
async function writeOpenClawPluginConfig(stateDir, config) {
|
|
5088
|
-
const openclawConfigPath =
|
|
5591
|
+
const openclawConfigPath = path12.join(stateDir, "openclaw.json");
|
|
5089
5592
|
let root = {};
|
|
5090
5593
|
try {
|
|
5091
|
-
const raw = await
|
|
5594
|
+
const raw = await fs7.readFile(openclawConfigPath, "utf8");
|
|
5092
5595
|
const parsed = JSON.parse(raw);
|
|
5093
5596
|
if (isRecord(parsed)) {
|
|
5094
5597
|
root = parsed;
|
|
@@ -5114,13 +5617,13 @@ async function writeOpenClawPluginConfig(stateDir, config) {
|
|
|
5114
5617
|
} else {
|
|
5115
5618
|
delete entryConfig.configPath;
|
|
5116
5619
|
}
|
|
5117
|
-
await
|
|
5118
|
-
await
|
|
5620
|
+
await fs7.mkdir(stateDir, { recursive: true });
|
|
5621
|
+
await fs7.writeFile(openclawConfigPath, `${JSON.stringify(root, null, 2)}
|
|
5119
5622
|
`, "utf8");
|
|
5120
5623
|
return openclawConfigPath;
|
|
5121
5624
|
}
|
|
5122
5625
|
function shouldPersistPluginConfigPath(dbPath, configPath) {
|
|
5123
|
-
return
|
|
5626
|
+
return path12.dirname(dbPath) !== path12.dirname(configPath);
|
|
5124
5627
|
}
|
|
5125
5628
|
function isRecord(value) {
|
|
5126
5629
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -5147,21 +5650,21 @@ function ensureArrayOfStrings(target, key) {
|
|
|
5147
5650
|
}
|
|
5148
5651
|
|
|
5149
5652
|
// src/cli/commands/init/openclaw-detect.ts
|
|
5150
|
-
import
|
|
5653
|
+
import fs8 from "fs";
|
|
5151
5654
|
import os3 from "os";
|
|
5152
|
-
import
|
|
5655
|
+
import path13 from "path";
|
|
5153
5656
|
function resolveDefaultOpenClawStateDir() {
|
|
5154
|
-
return
|
|
5657
|
+
return path13.join(os3.homedir(), ".openclaw");
|
|
5155
5658
|
}
|
|
5156
|
-
function detectOpenClawInstallation(env = process.env, existsSyncFn =
|
|
5659
|
+
function detectOpenClawInstallation(env = process.env, existsSyncFn = fs8.existsSync) {
|
|
5157
5660
|
const envStateDir = normalizeOptionalString5(env.OPENCLAW_STATE_DIR) ?? normalizeOptionalString5(env.OPENCLAW_HOME);
|
|
5158
5661
|
const source = envStateDir ? "environment" : "default";
|
|
5159
5662
|
const stateDir = envStateDir ? resolveUserPath2(envStateDir) : resolveDefaultOpenClawStateDir();
|
|
5160
5663
|
return {
|
|
5161
5664
|
detected: envStateDir !== void 0 || existsSyncFn(stateDir),
|
|
5162
5665
|
stateDir,
|
|
5163
|
-
configPath:
|
|
5164
|
-
sessionsRoot:
|
|
5666
|
+
configPath: path13.join(stateDir, "openclaw.json"),
|
|
5667
|
+
sessionsRoot: path13.join(stateDir, "agents"),
|
|
5165
5668
|
source
|
|
5166
5669
|
};
|
|
5167
5670
|
}
|
|
@@ -5171,8 +5674,8 @@ function normalizeOptionalString5(value) {
|
|
|
5171
5674
|
}
|
|
5172
5675
|
|
|
5173
5676
|
// src/cli/commands/init/session-scanner.ts
|
|
5174
|
-
import
|
|
5175
|
-
import
|
|
5677
|
+
import fs9 from "fs/promises";
|
|
5678
|
+
import path14 from "path";
|
|
5176
5679
|
async function scanSessionFiles(sessionsRoot, recentDays = 7) {
|
|
5177
5680
|
const result = {
|
|
5178
5681
|
totalFiles: 0,
|
|
@@ -5197,7 +5700,7 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
|
|
|
5197
5700
|
}
|
|
5198
5701
|
let stat;
|
|
5199
5702
|
try {
|
|
5200
|
-
stat = await
|
|
5703
|
+
stat = await fs9.stat(filePath);
|
|
5201
5704
|
} catch (error) {
|
|
5202
5705
|
if (isMissingPathError(error)) {
|
|
5203
5706
|
continue;
|
|
@@ -5215,7 +5718,7 @@ async function scanSessionFiles(sessionsRoot, recentDays = 7) {
|
|
|
5215
5718
|
return result;
|
|
5216
5719
|
}
|
|
5217
5720
|
function isSessionTranscriptPath(filePath) {
|
|
5218
|
-
return filePath.split(
|
|
5721
|
+
return filePath.split(path14.sep).includes("sessions");
|
|
5219
5722
|
}
|
|
5220
5723
|
function isMissingPathError(error) {
|
|
5221
5724
|
return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
@@ -5341,7 +5844,7 @@ async function runInitWizard(options = {}) {
|
|
|
5341
5844
|
});
|
|
5342
5845
|
prompts.log.info(`Updated ${formatPathForDisplay(openclawConfigPath)} to enable agenr as the active memory plugin.`);
|
|
5343
5846
|
} catch (error) {
|
|
5344
|
-
const message =
|
|
5847
|
+
const message = formatUnknownError7(error);
|
|
5345
5848
|
pluginStatus = `Plugin installed, but OpenClaw config update failed: ${message}`;
|
|
5346
5849
|
prompts.log.warn(`OpenClaw config update failed: ${message}`);
|
|
5347
5850
|
}
|
|
@@ -5358,18 +5861,18 @@ async function runInitWizard(options = {}) {
|
|
|
5358
5861
|
scanSpinner.start("Scanning existing OpenClaw sessions...");
|
|
5359
5862
|
sessionScan = await runtime.scanSessionFiles(detection.sessionsRoot);
|
|
5360
5863
|
scanSpinner.stop(
|
|
5361
|
-
`Found ${sessionScan.totalFiles} ${
|
|
5864
|
+
`Found ${sessionScan.totalFiles} ${pluralize3(sessionScan.totalFiles, "session")} under ${formatPathForDisplay(detection.sessionsRoot)}.`
|
|
5362
5865
|
);
|
|
5363
5866
|
if (sessionScan.totalFiles === 0) {
|
|
5364
5867
|
sessionStatus = "No sessions found";
|
|
5365
5868
|
ingestStatus = "Skipped - no sessions found";
|
|
5366
5869
|
prompts.log.info("No existing OpenClaw session transcripts were found. You can ingest later once sessions exist.");
|
|
5367
5870
|
} else if (!setupReady) {
|
|
5368
|
-
sessionStatus = `${sessionScan.totalFiles} ${
|
|
5871
|
+
sessionStatus = `${sessionScan.totalFiles} ${pluralize3(sessionScan.totalFiles, "session")} found (${sessionScan.recentFiles.length} from last 7 days)`;
|
|
5369
5872
|
ingestStatus = "Skipped - current auth still needs credentials";
|
|
5370
5873
|
prompts.log.warn(setupReadiness.guidance ?? "Skipping bulk ingest until agenr can resolve working LLM credentials for the selected auth profile.");
|
|
5371
5874
|
} else {
|
|
5372
|
-
sessionStatus = `${sessionScan.totalFiles} ${
|
|
5875
|
+
sessionStatus = `${sessionScan.totalFiles} ${pluralize3(sessionScan.totalFiles, "session")} found (${sessionScan.recentFiles.length} from last 7 days)`;
|
|
5373
5876
|
const { provider: extractionProvider, modelId } = resolveModel(activeConfig, "extraction");
|
|
5374
5877
|
const providerForCost = normalizeSetupProvider(extractionProvider);
|
|
5375
5878
|
const showUsdEstimate = hasMeteredIngestCost(activeConfig.auth);
|
|
@@ -5389,11 +5892,11 @@ async function runInitWizard(options = {}) {
|
|
|
5389
5892
|
} else {
|
|
5390
5893
|
const filesToIngest = ingestChoice === "recent" ? sessionScan.recentFiles : sessionScan.allFiles;
|
|
5391
5894
|
const ingestResult = await runtime.runBulkIngest(filesToIngest, activeConfig, prompts);
|
|
5392
|
-
ingestStatus = `${ingestResult.filesProcessed} ${
|
|
5895
|
+
ingestStatus = `${ingestResult.filesProcessed} ${pluralize3(ingestResult.filesProcessed, "session")} processed, ${ingestResult.storedEntries} ${pluralize3(ingestResult.storedEntries, "entry", "entries")} stored`;
|
|
5393
5896
|
prompts.log.info(
|
|
5394
5897
|
[
|
|
5395
|
-
formatLabel("Ingested", `${ingestResult.filesProcessed} ${
|
|
5396
|
-
formatLabel("Stored", `${ingestResult.storedEntries} ${
|
|
5898
|
+
formatLabel("Ingested", `${ingestResult.filesProcessed} ${pluralize3(ingestResult.filesProcessed, "session")}`),
|
|
5899
|
+
formatLabel("Stored", `${ingestResult.storedEntries} ${pluralize3(ingestResult.storedEntries, "entry", "entries")}`),
|
|
5397
5900
|
formatLabel("Failures", `${ingestResult.failedFiles}`),
|
|
5398
5901
|
formatLabel("Cost", formatCostUsd(ingestResult.totalCostUsd))
|
|
5399
5902
|
].join("\n")
|
|
@@ -5422,14 +5925,14 @@ async function runInitWizard(options = {}) {
|
|
|
5422
5925
|
prompts.outro(buildNextSteps(detection, pluginStatus, gatewayStatus, sessionScan));
|
|
5423
5926
|
} catch (error) {
|
|
5424
5927
|
process.exitCode = 1;
|
|
5425
|
-
prompts.log.error(
|
|
5928
|
+
prompts.log.error(formatUnknownError7(error));
|
|
5426
5929
|
prompts.outro(ui.error("Init failed"));
|
|
5427
5930
|
}
|
|
5428
5931
|
}
|
|
5429
5932
|
async function runBulkIngest(files, config, prompts) {
|
|
5430
5933
|
let database = null;
|
|
5431
|
-
const
|
|
5432
|
-
|
|
5934
|
+
const spinner6 = prompts.spinner();
|
|
5935
|
+
spinner6.start(`Ingesting ${files.length} ${pluralize3(files.length, "session")}... (0/${files.length} extracted)`);
|
|
5433
5936
|
try {
|
|
5434
5937
|
database = await createDatabase(config.dbPath ?? resolveDbPath(config));
|
|
5435
5938
|
const { provider, modelId } = resolveModel(config, "extraction");
|
|
@@ -5457,17 +5960,17 @@ async function runBulkIngest(files, config, prompts) {
|
|
|
5457
5960
|
claimExtractionConfig,
|
|
5458
5961
|
extractionContext: config.extractionContext,
|
|
5459
5962
|
onExtractionProgress: (completed, total) => {
|
|
5460
|
-
|
|
5963
|
+
spinner6.message(`Ingesting sessions... (${completed}/${total} extracted)`);
|
|
5461
5964
|
},
|
|
5462
5965
|
onBulkWriteProgress: (event) => {
|
|
5463
|
-
|
|
5966
|
+
spinner6.message(progressMessageForBulkWrite2(event.phase));
|
|
5464
5967
|
}
|
|
5465
5968
|
}
|
|
5466
5969
|
);
|
|
5467
5970
|
const storedEntries = Array.from(result.storeResults.values()).reduce((total, fileResult) => total + (fileResult.storeResult?.stored ?? 0), 0);
|
|
5468
5971
|
const failedFiles = result.extractionRuns.filter((run) => run.result.error !== void 0).length;
|
|
5469
5972
|
const totalCostUsd = result.extractionRuns.reduce((total, run) => total + run.usage.totalCost, 0) + result.dedupUsage.totalCost;
|
|
5470
|
-
|
|
5973
|
+
spinner6.stop(`Ingest complete: ${storedEntries} ${pluralize3(storedEntries, "entry", "entries")} stored.`);
|
|
5471
5974
|
return {
|
|
5472
5975
|
filesProcessed: files.length,
|
|
5473
5976
|
storedEntries,
|
|
@@ -5480,7 +5983,7 @@ async function runBulkIngest(files, config, prompts) {
|
|
|
5480
5983
|
}
|
|
5481
5984
|
function buildIngestChoiceMessage(scan, modelId, recentCost, fullCost, showUsdEstimate) {
|
|
5482
5985
|
const lines = [
|
|
5483
|
-
`Found ${scan.totalFiles} ${
|
|
5986
|
+
`Found ${scan.totalFiles} ${pluralize3(scan.totalFiles, "session")} (${scan.recentFiles.length} from last 7 days).`,
|
|
5484
5987
|
"",
|
|
5485
5988
|
showUsdEstimate ? `Estimated extraction cost with ${modelId}:` : `Estimated transcript volume with ${modelId}:`
|
|
5486
5989
|
];
|
|
@@ -5571,7 +6074,7 @@ function progressMessageForBulkWrite2(phase) {
|
|
|
5571
6074
|
return "Bulk ingest finalization complete...";
|
|
5572
6075
|
}
|
|
5573
6076
|
}
|
|
5574
|
-
function
|
|
6077
|
+
function formatUnknownError7(error) {
|
|
5575
6078
|
return error instanceof Error ? error.message : String(error);
|
|
5576
6079
|
}
|
|
5577
6080
|
|
|
@@ -5583,11 +6086,11 @@ function registerInitCommand(program2) {
|
|
|
5583
6086
|
}
|
|
5584
6087
|
|
|
5585
6088
|
// src/cli/commands/recall.ts
|
|
5586
|
-
import * as
|
|
6089
|
+
import * as clack6 from "@clack/prompts";
|
|
5587
6090
|
import { InvalidArgumentError as InvalidArgumentError4, Option as Option3 } from "commander";
|
|
5588
6091
|
function registerRecallCommand(program2) {
|
|
5589
6092
|
program2.command("recall <query>").description("Search the knowledge database with the v1 hybrid recall pipeline").addOption(new Option3("--limit <n>", "Max results").argParser(parsePositiveInteger).default(10)).addOption(new Option3("--threshold <n>", "Minimum score cutoff").argParser(parseUnitInterval).default(0)).addOption(new Option3("--budget <n>", "Max token budget").argParser(parsePositiveInteger)).addOption(new Option3("--types <types>", "Comma-separated entry types").argParser(parseEntryTypes)).addOption(new Option3("--tags <tags>", "Comma-separated tags").argParser(parseCsvList)).option("--since <date>", "Only entries after this date (ISO or relative like 7d)").option("--until <date>", "Only entries before this date").option("--around <date>", "Bias results toward this date").option("--as-of <date>", "Resolve current vs prior state at this reference time").addOption(new Option3("--around-radius <n>", "Gaussian radius in days").argParser(parsePositiveNumber).default(14)).option("--verbose", "Show score breakdowns").action(async (query, options) => {
|
|
5590
|
-
|
|
6093
|
+
clack6.intro(banner());
|
|
5591
6094
|
let db = null;
|
|
5592
6095
|
try {
|
|
5593
6096
|
const commandInput = normalizeRecallCommand(query, options);
|
|
@@ -5597,8 +6100,8 @@ function registerRecallCommand(program2) {
|
|
|
5597
6100
|
db = await createDatabase(dbPath);
|
|
5598
6101
|
const adapter = createRecallAdapter(db, embeddingClient);
|
|
5599
6102
|
let lastTraceSummary;
|
|
5600
|
-
const
|
|
5601
|
-
|
|
6103
|
+
const spinner6 = clack6.spinner();
|
|
6104
|
+
spinner6.start("Searching knowledge...");
|
|
5602
6105
|
const results = await recall(commandInput.request, adapter, {
|
|
5603
6106
|
trace: {
|
|
5604
6107
|
reportSummary(summary) {
|
|
@@ -5606,24 +6109,24 @@ function registerRecallCommand(program2) {
|
|
|
5606
6109
|
}
|
|
5607
6110
|
}
|
|
5608
6111
|
});
|
|
5609
|
-
|
|
6112
|
+
spinner6.stop(`Found ${results.length} ${pluralize4(results.length, "result")}.`);
|
|
5610
6113
|
if (lastTraceSummary?.degraded.active) {
|
|
5611
6114
|
for (const notice of lastTraceSummary.degraded.notices) {
|
|
5612
|
-
|
|
6115
|
+
clack6.log.warn(notice);
|
|
5613
6116
|
}
|
|
5614
6117
|
}
|
|
5615
6118
|
if (results.length === 0) {
|
|
5616
|
-
|
|
6119
|
+
clack6.outro("No matching entries found.");
|
|
5617
6120
|
return;
|
|
5618
6121
|
}
|
|
5619
6122
|
for (const result of results) {
|
|
5620
|
-
|
|
6123
|
+
clack6.log.step(formatResult(result, commandInput.verbose, commandInput.request.asOf));
|
|
5621
6124
|
}
|
|
5622
|
-
|
|
6125
|
+
clack6.outro(`Recall complete: ${results.length} ${pluralize4(results.length, "result")}.`);
|
|
5623
6126
|
} catch (error) {
|
|
5624
6127
|
process.exitCode = 1;
|
|
5625
|
-
|
|
5626
|
-
|
|
6128
|
+
clack6.log.error(formatUnknownError8(error));
|
|
6129
|
+
clack6.outro(ui.error("Recall failed"));
|
|
5627
6130
|
} finally {
|
|
5628
6131
|
await db?.close();
|
|
5629
6132
|
}
|
|
@@ -5710,10 +6213,10 @@ function formatProvenance(projected) {
|
|
|
5710
6213
|
].filter((value) => value !== void 0);
|
|
5711
6214
|
return parts.join(" ");
|
|
5712
6215
|
}
|
|
5713
|
-
function
|
|
6216
|
+
function pluralize4(value, singular, plural) {
|
|
5714
6217
|
return value === 1 ? singular : plural ?? `${singular}s`;
|
|
5715
6218
|
}
|
|
5716
|
-
function
|
|
6219
|
+
function formatUnknownError8(error) {
|
|
5717
6220
|
return error instanceof Error ? error.message : String(error);
|
|
5718
6221
|
}
|
|
5719
6222
|
|
|
@@ -5721,13 +6224,13 @@ function formatUnknownError6(error) {
|
|
|
5721
6224
|
import { InvalidArgumentError as InvalidArgumentError5, Option as Option4 } from "commander";
|
|
5722
6225
|
|
|
5723
6226
|
// src/app/scenarios/claim-keys/runtime.ts
|
|
5724
|
-
import { randomUUID as
|
|
6227
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
5725
6228
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
5726
|
-
import
|
|
6229
|
+
import path21 from "path";
|
|
5727
6230
|
import { getModel as getModel2 } from "@mariozechner/pi-ai";
|
|
5728
6231
|
|
|
5729
6232
|
// src/adapters/db/surgeon-run-log.ts
|
|
5730
|
-
import { randomUUID } from "crypto";
|
|
6233
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
5731
6234
|
|
|
5732
6235
|
// src/core/surgeon/domain/pass-types.ts
|
|
5733
6236
|
var SURGEON_PASS_TYPES = ["claim_key_quality", "proposal_resolution", "retirement", "supersession"];
|
|
@@ -5766,7 +6269,7 @@ function resolveSurgeonProposalApplyTarget(proposal) {
|
|
|
5766
6269
|
var SURGEON_RUN_STATUSES = ["running", "completed", "failed", "aborted", "budget_exhausted", "cost_capped", "no_work", "stalled"];
|
|
5767
6270
|
var SURGEON_PROPOSAL_REVIEW_STATUSES = ["open", "applied", "rejected"];
|
|
5768
6271
|
async function createSurgeonRun(executor, run) {
|
|
5769
|
-
const id =
|
|
6272
|
+
const id = randomUUID2();
|
|
5770
6273
|
const startedAt = normalizeTimestamp(run.startedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5771
6274
|
await executor.execute({
|
|
5772
6275
|
sql: `
|
|
@@ -5844,7 +6347,7 @@ async function logSurgeonAction(executor, action) {
|
|
|
5844
6347
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5845
6348
|
`,
|
|
5846
6349
|
args: [
|
|
5847
|
-
action.id.trim().length > 0 ? action.id.trim() :
|
|
6350
|
+
action.id.trim().length > 0 ? action.id.trim() : randomUUID2(),
|
|
5848
6351
|
action.runId.trim(),
|
|
5849
6352
|
action.actionType,
|
|
5850
6353
|
entryIds[0] ?? null,
|
|
@@ -5943,7 +6446,7 @@ async function logSurgeonProposal(executor, proposal) {
|
|
|
5943
6446
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5944
6447
|
`,
|
|
5945
6448
|
args: [
|
|
5946
|
-
proposal.id.trim().length > 0 ? proposal.id.trim() :
|
|
6449
|
+
proposal.id.trim().length > 0 ? proposal.id.trim() : randomUUID2(),
|
|
5947
6450
|
proposal.runId.trim(),
|
|
5948
6451
|
logicalIssue.groupId,
|
|
5949
6452
|
logicalIssue.issueKind,
|
|
@@ -7209,9 +7712,9 @@ function createSurgeonPort(executor) {
|
|
|
7209
7712
|
}
|
|
7210
7713
|
|
|
7211
7714
|
// src/app/surgeon/service.ts
|
|
7212
|
-
import { randomUUID as
|
|
7213
|
-
import
|
|
7214
|
-
import
|
|
7715
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
7716
|
+
import fs11 from "fs";
|
|
7717
|
+
import path16 from "path";
|
|
7215
7718
|
import { runAgentLoop } from "@mariozechner/pi-agent-core";
|
|
7216
7719
|
|
|
7217
7720
|
// src/core/surgeon/domain/run-presets.ts
|
|
@@ -7269,7 +7772,7 @@ function normalizeUsageAmount(value) {
|
|
|
7269
7772
|
}
|
|
7270
7773
|
|
|
7271
7774
|
// src/app/surgeon/claim-key-quality.ts
|
|
7272
|
-
import { randomUUID as
|
|
7775
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7273
7776
|
|
|
7274
7777
|
// src/core/claim-key-slot-resonance.ts
|
|
7275
7778
|
var FAMILY_GENERIC_TOKEN_MIN_COUNT = 3;
|
|
@@ -8445,7 +8948,7 @@ async function runClaimKeyQualityPass(options, deps) {
|
|
|
8445
8948
|
}
|
|
8446
8949
|
applyClaimKeyLifecycle(actual, lifecycle);
|
|
8447
8950
|
await deps.port.logRunAction({
|
|
8448
|
-
id:
|
|
8951
|
+
id: randomUUID3(),
|
|
8449
8952
|
runId: options.runId,
|
|
8450
8953
|
actionType: "update_entry",
|
|
8451
8954
|
entryIds: [entryId],
|
|
@@ -8472,7 +8975,7 @@ async function runClaimKeyQualityPass(options, deps) {
|
|
|
8472
8975
|
async function persistProposal(proposal, audit) {
|
|
8473
8976
|
await deps.port.logRunProposal(proposal);
|
|
8474
8977
|
await deps.port.logRunAction({
|
|
8475
|
-
id:
|
|
8978
|
+
id: randomUUID3(),
|
|
8476
8979
|
runId: options.runId,
|
|
8477
8980
|
actionType: "flag_review",
|
|
8478
8981
|
entryIds: proposal.entryIds,
|
|
@@ -9357,7 +9860,7 @@ function restoreClaimKeyLifecycle(entry, snapshot) {
|
|
|
9357
9860
|
}
|
|
9358
9861
|
function createProposal(input) {
|
|
9359
9862
|
return {
|
|
9360
|
-
id:
|
|
9863
|
+
id: randomUUID3(),
|
|
9361
9864
|
...input,
|
|
9362
9865
|
entryIds: normalizeStringArray3(input.entryIds),
|
|
9363
9866
|
currentClaimKeys: normalizeStringArray3(input.currentClaimKeys),
|
|
@@ -9689,9 +10192,9 @@ function createSupersessionReviewTracker(input) {
|
|
|
9689
10192
|
progress = createEmptySupersessionProgress(input);
|
|
9690
10193
|
entryToClusterKeys = /* @__PURE__ */ new Map();
|
|
9691
10194
|
},
|
|
9692
|
-
recordPage({ scope,
|
|
9693
|
-
const normalizedClaimKeyRemaining = normalizeCount(
|
|
9694
|
-
const normalizedSubjectRemaining = normalizeCount(
|
|
10195
|
+
recordPage({ scope, claimKeyRemaining, subjectRemaining, clusters }) {
|
|
10196
|
+
const normalizedClaimKeyRemaining = normalizeCount(claimKeyRemaining);
|
|
10197
|
+
const normalizedSubjectRemaining = normalizeCount(subjectRemaining);
|
|
9695
10198
|
const nextClaimKeyViewed = new Set(progress.claimKeyViewedKeys);
|
|
9696
10199
|
const nextSubjectViewed = new Set(progress.subjectViewedKeys);
|
|
9697
10200
|
const nextEntryMap = cloneEntryClusterMap(entryToClusterKeys);
|
|
@@ -9873,7 +10376,7 @@ function normalizeOptionalCount(value) {
|
|
|
9873
10376
|
}
|
|
9874
10377
|
|
|
9875
10378
|
// src/app/surgeon/proposal-review.ts
|
|
9876
|
-
import { randomUUID as
|
|
10379
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
9877
10380
|
async function loadActiveProposalEntries(proposal, getEntry2) {
|
|
9878
10381
|
const activeEntries = [];
|
|
9879
10382
|
const inactiveEntryIds = [];
|
|
@@ -9913,7 +10416,7 @@ async function applyProposalToEntries(input, deps) {
|
|
|
9913
10416
|
}
|
|
9914
10417
|
if (updatedEntryIds.length > 0) {
|
|
9915
10418
|
await deps.logRunAction({
|
|
9916
|
-
id:
|
|
10419
|
+
id: randomUUID4(),
|
|
9917
10420
|
runId: input.actionRunId ?? input.proposal.runId,
|
|
9918
10421
|
actionType: "update_entry",
|
|
9919
10422
|
entryIds: updatedEntryIds,
|
|
@@ -9937,8 +10440,8 @@ async function applyProposalToEntries(input, deps) {
|
|
|
9937
10440
|
}
|
|
9938
10441
|
|
|
9939
10442
|
// src/app/surgeon/trace-logger.ts
|
|
9940
|
-
import
|
|
9941
|
-
import
|
|
10443
|
+
import fs10 from "fs";
|
|
10444
|
+
import path15 from "path";
|
|
9942
10445
|
var TRACE_MAX_STRING_LENGTH = 320;
|
|
9943
10446
|
var TRACE_MAX_ARRAY_ITEMS = 12;
|
|
9944
10447
|
var TRACE_MAX_OBJECT_KEYS = 20;
|
|
@@ -10146,8 +10649,8 @@ function appendTrace(tracePath, payload, logger) {
|
|
|
10146
10649
|
return;
|
|
10147
10650
|
}
|
|
10148
10651
|
try {
|
|
10149
|
-
|
|
10150
|
-
|
|
10652
|
+
fs10.mkdirSync(path15.dirname(tracePath), { recursive: true });
|
|
10653
|
+
fs10.appendFileSync(tracePath, `${JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...payload })}
|
|
10151
10654
|
`, "utf8");
|
|
10152
10655
|
} catch (error) {
|
|
10153
10656
|
logger.warn(`failed to write trace file ${tracePath}: ${formatError2(error)}`);
|
|
@@ -10557,7 +11060,7 @@ function getSurgeonSupersessionPassPrompt() {
|
|
|
10557
11060
|
}
|
|
10558
11061
|
|
|
10559
11062
|
// src/app/surgeon/tools/complete.ts
|
|
10560
|
-
import { randomUUID as
|
|
11063
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
10561
11064
|
import { Type } from "@sinclair/typebox";
|
|
10562
11065
|
|
|
10563
11066
|
// src/app/surgeon/tools/shared.ts
|
|
@@ -10616,7 +11119,7 @@ function createCompletePassTool(deps) {
|
|
|
10616
11119
|
deps.completionGuards?.supersession.markAdjudicated([entryId]);
|
|
10617
11120
|
}
|
|
10618
11121
|
await deps.recordRunAction({
|
|
10619
|
-
id:
|
|
11122
|
+
id: randomUUID5(),
|
|
10620
11123
|
runId: deps.runId,
|
|
10621
11124
|
actionType: "skip",
|
|
10622
11125
|
entryIds: entryId ? [entryId] : [],
|
|
@@ -11207,7 +11710,7 @@ function normalizeOptionalString10(value) {
|
|
|
11207
11710
|
}
|
|
11208
11711
|
|
|
11209
11712
|
// src/app/surgeon/tools/supersession-claim.ts
|
|
11210
|
-
import { randomUUID as
|
|
11713
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
11211
11714
|
import { Type as Type7 } from "@sinclair/typebox";
|
|
11212
11715
|
var ASSIGN_CLAIM_KEY_SCHEMA = Type7.Object({
|
|
11213
11716
|
entry_id: Type7.String({ minLength: 1 }),
|
|
@@ -11286,7 +11789,7 @@ function createAssignClaimKeyTool(deps) {
|
|
|
11286
11789
|
if (updated) {
|
|
11287
11790
|
deps.completionGuards?.supersession.markAdjudicated([entry.id]);
|
|
11288
11791
|
await deps.recordRunAction({
|
|
11289
|
-
id:
|
|
11792
|
+
id: randomUUID6(),
|
|
11290
11793
|
runId: deps.runId,
|
|
11291
11794
|
actionType: "update_entry",
|
|
11292
11795
|
entryIds: [entry.id],
|
|
@@ -11307,7 +11810,7 @@ function createAssignClaimKeyTool(deps) {
|
|
|
11307
11810
|
}
|
|
11308
11811
|
|
|
11309
11812
|
// src/app/surgeon/tools/supersession-link.ts
|
|
11310
|
-
import { randomUUID as
|
|
11813
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
11311
11814
|
import { Type as Type8 } from "@sinclair/typebox";
|
|
11312
11815
|
var LINK_SUPERSESSION_SCHEMA = Type8.Object({
|
|
11313
11816
|
old_entry_id: Type8.String({ minLength: 1, description: "Entry being superseded." }),
|
|
@@ -11418,7 +11921,7 @@ function createLinkSupersessionTool(deps) {
|
|
|
11418
11921
|
}
|
|
11419
11922
|
deps.completionGuards?.supersession.markAdjudicated([oldEntryId, newEntryId]);
|
|
11420
11923
|
await deps.recordRunAction({
|
|
11421
|
-
id:
|
|
11924
|
+
id: randomUUID7(),
|
|
11422
11925
|
runId: deps.runId,
|
|
11423
11926
|
actionType: "resolve_conflict",
|
|
11424
11927
|
entryIds: [oldEntryId, newEntryId],
|
|
@@ -11519,10 +12022,13 @@ function createQuerySupersessionCandidatesTool(deps) {
|
|
|
11519
12022
|
const subjectClusterCount = scope === "claim_key" ? progress?.subjectClustersRemaining ?? counts.subjectCount : pendingSubjectClusters.length;
|
|
11520
12023
|
const allClusters = scope === "claim_key" ? pendingClaimKeyClusters : scope === "subject" ? pendingSubjectClusters : [...pendingClaimKeyClusters, ...pendingSubjectClusters];
|
|
11521
12024
|
const clusters = allClusters.slice(offset, offset + limit);
|
|
12025
|
+
const remainingClusters = allClusters.slice(Math.min(allClusters.length, offset + clusters.length));
|
|
12026
|
+
const claimKeyRemaining = scope === "subject" ? claimKeyClusterCount : countClustersByGrouping(remainingClusters, "claim_key");
|
|
12027
|
+
const subjectRemaining = scope === "claim_key" ? subjectClusterCount : countClustersByGrouping(remainingClusters, "subject");
|
|
11522
12028
|
deps.completionGuards?.supersession.recordPage({
|
|
11523
12029
|
scope,
|
|
11524
|
-
|
|
11525
|
-
|
|
12030
|
+
claimKeyRemaining,
|
|
12031
|
+
subjectRemaining,
|
|
11526
12032
|
clusters
|
|
11527
12033
|
});
|
|
11528
12034
|
if (clusters.length === 0) {
|
|
@@ -11590,9 +12096,18 @@ function normalizeOptionalString11(value) {
|
|
|
11590
12096
|
const normalized = value?.trim();
|
|
11591
12097
|
return normalized ? normalized : void 0;
|
|
11592
12098
|
}
|
|
12099
|
+
function countClustersByGrouping(clusters, groupedBy) {
|
|
12100
|
+
let count = 0;
|
|
12101
|
+
for (const cluster of clusters) {
|
|
12102
|
+
if (cluster.groupedBy === groupedBy) {
|
|
12103
|
+
count += 1;
|
|
12104
|
+
}
|
|
12105
|
+
}
|
|
12106
|
+
return count;
|
|
12107
|
+
}
|
|
11593
12108
|
|
|
11594
12109
|
// src/app/surgeon/tools/supersession-validity.ts
|
|
11595
|
-
import { randomUUID as
|
|
12110
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
11596
12111
|
import { Type as Type10 } from "@sinclair/typebox";
|
|
11597
12112
|
var SET_VALIDITY_SCHEMA = Type10.Object({
|
|
11598
12113
|
entry_id: Type10.String({ minLength: 1 }),
|
|
@@ -11701,7 +12216,7 @@ function createSetValidityTool(deps) {
|
|
|
11701
12216
|
if (updated) {
|
|
11702
12217
|
deps.completionGuards?.supersession.markAdjudicated([entry.id]);
|
|
11703
12218
|
await deps.recordRunAction({
|
|
11704
|
-
id:
|
|
12219
|
+
id: randomUUID8(),
|
|
11705
12220
|
runId: deps.runId,
|
|
11706
12221
|
actionType: "update_entry",
|
|
11707
12222
|
entryIds: [entry.id],
|
|
@@ -12561,7 +13076,7 @@ async function runSurgeon(options, deps) {
|
|
|
12561
13076
|
const entryIds = extractEntryIds(context.args);
|
|
12562
13077
|
const reasoning = extractActionReasoning(context.assistantMessage, context.args, context.result.details, context.toolCall.name);
|
|
12563
13078
|
const action = {
|
|
12564
|
-
id:
|
|
13079
|
+
id: randomUUID9(),
|
|
12565
13080
|
runId,
|
|
12566
13081
|
actionType,
|
|
12567
13082
|
entryIds,
|
|
@@ -12684,8 +13199,8 @@ function resolveTracePath(tracePath, passType, runId) {
|
|
|
12684
13199
|
return void 0;
|
|
12685
13200
|
}
|
|
12686
13201
|
try {
|
|
12687
|
-
if (
|
|
12688
|
-
return
|
|
13202
|
+
if (fs11.statSync(tracePath).isDirectory()) {
|
|
13203
|
+
return path16.join(tracePath, `surgeon-${passType}-${runId}.jsonl`);
|
|
12689
13204
|
}
|
|
12690
13205
|
} catch {
|
|
12691
13206
|
return tracePath;
|
|
@@ -12901,7 +13416,7 @@ async function runProposalResolutionPass(input, deps) {
|
|
|
12901
13416
|
}
|
|
12902
13417
|
if (!input.apply) {
|
|
12903
13418
|
await deps.recordRunAction({
|
|
12904
|
-
id:
|
|
13419
|
+
id: randomUUID9(),
|
|
12905
13420
|
runId: input.runId,
|
|
12906
13421
|
actionType: "update_entry",
|
|
12907
13422
|
entryIds: proposalEntryIds,
|
|
@@ -13687,7 +14202,7 @@ function isFixtureError(value) {
|
|
|
13687
14202
|
|
|
13688
14203
|
// src/app/scenarios/claim-keys/fixture-loader.ts
|
|
13689
14204
|
import { readFile } from "fs/promises";
|
|
13690
|
-
import
|
|
14205
|
+
import path18 from "path";
|
|
13691
14206
|
|
|
13692
14207
|
// src/app/scenarios/claim-keys/validation/shared.ts
|
|
13693
14208
|
var SUPPORTED_PROPOSAL_SCOPES = ["single_entry", "cluster"];
|
|
@@ -14017,12 +14532,12 @@ function readRequiredTrue(value, label, filePath) {
|
|
|
14017
14532
|
|
|
14018
14533
|
// src/app/scenarios/claim-keys/validation/scenario-root.ts
|
|
14019
14534
|
import { existsSync } from "fs";
|
|
14020
|
-
import
|
|
14535
|
+
import path17 from "path";
|
|
14021
14536
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14022
14537
|
var SCENARIO_ROOT_SEGMENTS = ["tests", "scenarios", "claim-keys"];
|
|
14023
14538
|
function getDefaultClaimKeyScenarioRoot(options = {}) {
|
|
14024
|
-
const moduleDirectory =
|
|
14025
|
-
const startDirectories = Array.from(/* @__PURE__ */ new Set([
|
|
14539
|
+
const moduleDirectory = path17.dirname(fileURLToPath2(options.moduleUrl ?? import.meta.url));
|
|
14540
|
+
const startDirectories = Array.from(/* @__PURE__ */ new Set([path17.resolve(options.cwd ?? process.cwd()), moduleDirectory]));
|
|
14026
14541
|
for (const startDirectory of startDirectories) {
|
|
14027
14542
|
const discovered = findScenarioRootFrom(startDirectory);
|
|
14028
14543
|
if (discovered) {
|
|
@@ -14044,24 +14559,24 @@ function readOptionalRelativeFixturePath(value, label, filePath, rootDir) {
|
|
|
14044
14559
|
return readRelativeFixturePath(value, label, filePath, rootDir);
|
|
14045
14560
|
}
|
|
14046
14561
|
function normalizeFixturePath(relativePath, rootDir, filePath, label) {
|
|
14047
|
-
if (
|
|
14562
|
+
if (path17.isAbsolute(relativePath)) {
|
|
14048
14563
|
throw new Error(`Invalid scenario ${filePath}: ${label} must be relative to the scenario root.`);
|
|
14049
14564
|
}
|
|
14050
|
-
const resolved =
|
|
14051
|
-
const relative =
|
|
14052
|
-
if (relative.startsWith("..") ||
|
|
14565
|
+
const resolved = path17.resolve(rootDir, relativePath);
|
|
14566
|
+
const relative = path17.relative(rootDir, resolved);
|
|
14567
|
+
if (relative.startsWith("..") || path17.isAbsolute(relative)) {
|
|
14053
14568
|
throw new Error(`Invalid scenario ${filePath}: ${label} must stay inside the scenario root.`);
|
|
14054
14569
|
}
|
|
14055
|
-
return relative.split(
|
|
14570
|
+
return relative.split(path17.sep).join("/");
|
|
14056
14571
|
}
|
|
14057
14572
|
function findScenarioRootFrom(startDirectory) {
|
|
14058
|
-
let currentDirectory =
|
|
14573
|
+
let currentDirectory = path17.resolve(startDirectory);
|
|
14059
14574
|
while (true) {
|
|
14060
|
-
const candidate =
|
|
14575
|
+
const candidate = path17.join(currentDirectory, ...SCENARIO_ROOT_SEGMENTS);
|
|
14061
14576
|
if (existsSync(candidate)) {
|
|
14062
14577
|
return candidate;
|
|
14063
14578
|
}
|
|
14064
|
-
const parentDirectory =
|
|
14579
|
+
const parentDirectory = path17.dirname(currentDirectory);
|
|
14065
14580
|
if (parentDirectory === currentDirectory) {
|
|
14066
14581
|
return void 0;
|
|
14067
14582
|
}
|
|
@@ -14404,7 +14919,7 @@ async function loadSeedFixtureEntries(rootDir, relativePath) {
|
|
|
14404
14919
|
if (!relativePath) {
|
|
14405
14920
|
return null;
|
|
14406
14921
|
}
|
|
14407
|
-
const parsed = await readJsonFile(
|
|
14922
|
+
const parsed = await readJsonFile(path18.join(rootDir, relativePath));
|
|
14408
14923
|
const seedEntries = readSeedEntries(parsed, relativePath);
|
|
14409
14924
|
if (!seedEntries) {
|
|
14410
14925
|
throw new Error(`Seed fixture ${relativePath} must contain an array.`);
|
|
@@ -14467,7 +14982,7 @@ async function readArrayFixture(rootDir, relativePath) {
|
|
|
14467
14982
|
if (!relativePath) {
|
|
14468
14983
|
return null;
|
|
14469
14984
|
}
|
|
14470
|
-
const parsed = await readJsonFile(
|
|
14985
|
+
const parsed = await readJsonFile(path18.join(rootDir, relativePath));
|
|
14471
14986
|
if (!Array.isArray(parsed)) {
|
|
14472
14987
|
throw new Error(`Fixture file ${relativePath} must contain a JSON array.`);
|
|
14473
14988
|
}
|
|
@@ -14521,7 +15036,7 @@ function readExtractionImportance(value, label, filePath) {
|
|
|
14521
15036
|
|
|
14522
15037
|
// src/app/scenarios/claim-keys/load-scenarios.ts
|
|
14523
15038
|
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
14524
|
-
import
|
|
15039
|
+
import path19 from "path";
|
|
14525
15040
|
|
|
14526
15041
|
// src/app/scenarios/claim-keys/validation/expectations.ts
|
|
14527
15042
|
var EXPECTATION_KEYS = /* @__PURE__ */ new Set(["warnings", "rows", "rowCount", "proposals", "storeResult", "surgeonSummary"]);
|
|
@@ -14783,13 +15298,13 @@ function validateClaimKeyScenario(input, filePath, rootDir = getDefaultClaimKeyS
|
|
|
14783
15298
|
async function discoverScenarioFiles(rootDir) {
|
|
14784
15299
|
const files = [];
|
|
14785
15300
|
for (const kind of SUPPORTED_KINDS2) {
|
|
14786
|
-
const directory =
|
|
15301
|
+
const directory = path19.join(rootDir, kind);
|
|
14787
15302
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
14788
15303
|
for (const entry of entries) {
|
|
14789
15304
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
14790
15305
|
continue;
|
|
14791
15306
|
}
|
|
14792
|
-
files.push(
|
|
15307
|
+
files.push(path19.join(directory, entry.name));
|
|
14793
15308
|
}
|
|
14794
15309
|
}
|
|
14795
15310
|
return files.sort();
|
|
@@ -14807,11 +15322,11 @@ async function readScenarioJsonFile(filePath) {
|
|
|
14807
15322
|
|
|
14808
15323
|
// src/app/scenarios/claim-keys/sandbox.ts
|
|
14809
15324
|
import { mkdir, rm } from "fs/promises";
|
|
14810
|
-
import
|
|
15325
|
+
import path20 from "path";
|
|
14811
15326
|
var SANDBOX_DB_FILENAME = "knowledge.db";
|
|
14812
15327
|
async function createClaimKeyScenarioSandbox(root) {
|
|
14813
|
-
const resolvedRoot =
|
|
14814
|
-
const dbPath =
|
|
15328
|
+
const resolvedRoot = path20.resolve(root);
|
|
15329
|
+
const dbPath = path20.join(resolvedRoot, SANDBOX_DB_FILENAME);
|
|
14815
15330
|
await mkdir(resolvedRoot, { recursive: true });
|
|
14816
15331
|
await removeDatabaseFiles(dbPath);
|
|
14817
15332
|
const database = await createDatabase(dbPath);
|
|
@@ -14833,7 +15348,7 @@ async function removeDatabaseFiles(dbPath) {
|
|
|
14833
15348
|
}
|
|
14834
15349
|
|
|
14835
15350
|
// src/app/scenarios/claim-keys/seed.ts
|
|
14836
|
-
import { randomUUID as
|
|
15351
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
14837
15352
|
var DEFAULT_SCENARIO_CREATED_AT = "2026-04-01T10:00:00.000Z";
|
|
14838
15353
|
function buildClaimKeyScenarioSeedEntry(seedEntry) {
|
|
14839
15354
|
const seedClaimKey = normalizeOptionalString12(seedEntry.claim_key);
|
|
@@ -14843,7 +15358,7 @@ function buildClaimKeyScenarioSeedEntry(seedEntry) {
|
|
|
14843
15358
|
const createdAt = seedEntry.created_at?.trim() || validatedInput.created_at || DEFAULT_SCENARIO_CREATED_AT;
|
|
14844
15359
|
const updatedAt = seedEntry.updated_at?.trim() || createdAt;
|
|
14845
15360
|
return {
|
|
14846
|
-
id: seedEntry.id?.trim() ||
|
|
15361
|
+
id: seedEntry.id?.trim() || randomUUID10(),
|
|
14847
15362
|
type: validatedInput.type,
|
|
14848
15363
|
subject: validatedInput.subject,
|
|
14849
15364
|
content: validatedInput.content,
|
|
@@ -15002,7 +15517,7 @@ async function listClaimKeyScenariosRuntime(options = {}) {
|
|
|
15002
15517
|
}
|
|
15003
15518
|
async function runClaimKeyScenariosRuntime(options = {}) {
|
|
15004
15519
|
const runId = buildRunId();
|
|
15005
|
-
const artifactRoot =
|
|
15520
|
+
const artifactRoot = path21.resolve(DEFAULT_ARTIFACT_ROOT, runId);
|
|
15006
15521
|
await mkdir2(artifactRoot, { recursive: true });
|
|
15007
15522
|
let scenarios;
|
|
15008
15523
|
try {
|
|
@@ -15054,11 +15569,11 @@ function filterClaimKeyScenarios(scenarios, options) {
|
|
|
15054
15569
|
}
|
|
15055
15570
|
async function runOneClaimKeyScenario(scenario, options) {
|
|
15056
15571
|
const startedAt = Date.now();
|
|
15057
|
-
const scenarioArtifactRoot =
|
|
15058
|
-
const sandboxRoot =
|
|
15572
|
+
const scenarioArtifactRoot = path21.join(options.artifactRoot, scenario.id);
|
|
15573
|
+
const sandboxRoot = path21.join(scenarioArtifactRoot, "sandbox");
|
|
15059
15574
|
const warnings = [];
|
|
15060
15575
|
await mkdir2(scenarioArtifactRoot, { recursive: true });
|
|
15061
|
-
await writeJson(
|
|
15576
|
+
await writeJson(path21.join(scenarioArtifactRoot, "scenario.json"), scenario);
|
|
15062
15577
|
let actual = {
|
|
15063
15578
|
warnings: [],
|
|
15064
15579
|
rows: [],
|
|
@@ -15171,7 +15686,7 @@ async function runIngestScenario(scenario, database, warnings, rootDir) {
|
|
|
15171
15686
|
throw new Error(`Scenario ${scenario.id} is missing extraction fixture responses.`);
|
|
15172
15687
|
}
|
|
15173
15688
|
const result = await ingestPath(
|
|
15174
|
-
|
|
15689
|
+
path21.join(rootDir, scenario.input.transcriptFile),
|
|
15175
15690
|
{
|
|
15176
15691
|
files: localTranscriptFiles,
|
|
15177
15692
|
transcript: openClawTranscriptParser,
|
|
@@ -15313,20 +15828,20 @@ async function loadLatestSurgeonProposals(database, runId) {
|
|
|
15313
15828
|
}));
|
|
15314
15829
|
}
|
|
15315
15830
|
async function writeScenarioArtifacts(scenarioArtifactRoot, actual, assertionResults, diffSummary) {
|
|
15316
|
-
await writeJson(
|
|
15317
|
-
await writeJson(
|
|
15831
|
+
await writeJson(path21.join(scenarioArtifactRoot, "actual.json"), actual);
|
|
15832
|
+
await writeJson(path21.join(scenarioArtifactRoot, "diff.json"), {
|
|
15318
15833
|
assertions: assertionResults,
|
|
15319
15834
|
diffSummary
|
|
15320
15835
|
});
|
|
15321
|
-
await writeJson(
|
|
15836
|
+
await writeJson(path21.join(scenarioArtifactRoot, "warnings.json"), actual.warnings);
|
|
15322
15837
|
if (actual.storeResult) {
|
|
15323
|
-
await writeJson(
|
|
15838
|
+
await writeJson(path21.join(scenarioArtifactRoot, "store-result.json"), actual.storeResult);
|
|
15324
15839
|
}
|
|
15325
15840
|
if (actual.surgeonSummary) {
|
|
15326
|
-
await writeJson(
|
|
15841
|
+
await writeJson(path21.join(scenarioArtifactRoot, "surgeon-summary.json"), actual.surgeonSummary);
|
|
15327
15842
|
}
|
|
15328
15843
|
if (actual.proposals.length > 0) {
|
|
15329
|
-
await writeJson(
|
|
15844
|
+
await writeJson(path21.join(scenarioArtifactRoot, "proposals.json"), actual.proposals);
|
|
15330
15845
|
}
|
|
15331
15846
|
}
|
|
15332
15847
|
async function writeJson(filePath, value) {
|
|
@@ -15334,7 +15849,7 @@ async function writeJson(filePath, value) {
|
|
|
15334
15849
|
`, "utf8");
|
|
15335
15850
|
}
|
|
15336
15851
|
function buildRunId() {
|
|
15337
|
-
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/gu, "-")}-${
|
|
15852
|
+
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/gu, "-")}-${randomUUID11().slice(0, 8)}`;
|
|
15338
15853
|
}
|
|
15339
15854
|
function toConfigurationError(error) {
|
|
15340
15855
|
if (error instanceof ClaimKeyScenarioConfigurationError) {
|
|
@@ -15361,7 +15876,7 @@ function registerScenariosCommand(program2, deps = {}) {
|
|
|
15361
15876
|
` : renderScenarioList(scenarios));
|
|
15362
15877
|
} catch (error) {
|
|
15363
15878
|
process.exitCode = error instanceof ClaimKeyScenarioConfigurationError ? 2 : 1;
|
|
15364
|
-
stderr.write(`Scenario list failed: ${
|
|
15879
|
+
stderr.write(`Scenario list failed: ${formatUnknownError9(error)}
|
|
15365
15880
|
`);
|
|
15366
15881
|
}
|
|
15367
15882
|
});
|
|
@@ -15384,7 +15899,7 @@ function registerScenariosCommand(program2, deps = {}) {
|
|
|
15384
15899
|
` : renderScenarioRunSummary(summary));
|
|
15385
15900
|
} catch (error) {
|
|
15386
15901
|
process.exitCode = error instanceof ClaimKeyScenarioConfigurationError ? 2 : 1;
|
|
15387
|
-
stderr.write(`Scenario run failed: ${
|
|
15902
|
+
stderr.write(`Scenario run failed: ${formatUnknownError9(error)}
|
|
15388
15903
|
`);
|
|
15389
15904
|
}
|
|
15390
15905
|
});
|
|
@@ -15449,7 +15964,7 @@ function parseScenarioKind(value) {
|
|
|
15449
15964
|
}
|
|
15450
15965
|
return normalized;
|
|
15451
15966
|
}
|
|
15452
|
-
function
|
|
15967
|
+
function formatUnknownError9(error) {
|
|
15453
15968
|
return error instanceof Error ? error.message : String(error);
|
|
15454
15969
|
}
|
|
15455
15970
|
|
|
@@ -15458,7 +15973,7 @@ import { InvalidArgumentError as InvalidArgumentError6, Option as Option5 } from
|
|
|
15458
15973
|
|
|
15459
15974
|
// src/app/surgeon/runtime.ts
|
|
15460
15975
|
import { copyFile, mkdir as mkdir3 } from "fs/promises";
|
|
15461
|
-
import
|
|
15976
|
+
import path22 from "path";
|
|
15462
15977
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15463
15978
|
import { getModel as getModel3 } from "@mariozechner/pi-ai";
|
|
15464
15979
|
var DEFAULT_SURGEON_PROVIDER = "openai";
|
|
@@ -15703,7 +16218,7 @@ async function backupDatabaseFile(dbPath) {
|
|
|
15703
16218
|
}
|
|
15704
16219
|
const sourcePath = resolveFilesystemPath(dbPath);
|
|
15705
16220
|
const backupPath = `${sourcePath}.surgeon-backup-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-")}`;
|
|
15706
|
-
await mkdir3(
|
|
16221
|
+
await mkdir3(path22.dirname(backupPath), { recursive: true });
|
|
15707
16222
|
await copyFile(sourcePath, backupPath);
|
|
15708
16223
|
await copySidecarIfPresent(`${sourcePath}-wal`, `${backupPath}-wal`);
|
|
15709
16224
|
await copySidecarIfPresent(`${sourcePath}-shm`, `${backupPath}-shm`);
|
|
@@ -15721,12 +16236,12 @@ async function copySidecarIfPresent(sourcePath, targetPath) {
|
|
|
15721
16236
|
}
|
|
15722
16237
|
function resolveFilesystemPath(value) {
|
|
15723
16238
|
if (!value.startsWith("file:")) {
|
|
15724
|
-
return
|
|
16239
|
+
return path22.resolve(value);
|
|
15725
16240
|
}
|
|
15726
16241
|
try {
|
|
15727
16242
|
return fileURLToPath3(value);
|
|
15728
16243
|
} catch {
|
|
15729
|
-
return
|
|
16244
|
+
return path22.resolve(value.slice("file:".length));
|
|
15730
16245
|
}
|
|
15731
16246
|
}
|
|
15732
16247
|
function isMissingFileError2(error) {
|
|
@@ -15778,7 +16293,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15778
16293
|
` : renderRunResult(result, commandInput.apply));
|
|
15779
16294
|
} catch (error) {
|
|
15780
16295
|
process.exitCode = 1;
|
|
15781
|
-
process.stderr.write(`Surgeon run failed: ${
|
|
16296
|
+
process.stderr.write(`Surgeon run failed: ${formatUnknownError10(error)}
|
|
15782
16297
|
`);
|
|
15783
16298
|
} finally {
|
|
15784
16299
|
display?.dispose();
|
|
@@ -15793,7 +16308,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15793
16308
|
process.stdout.write(renderStatus(result));
|
|
15794
16309
|
} catch (error) {
|
|
15795
16310
|
process.exitCode = 1;
|
|
15796
|
-
process.stderr.write(`Failed to load surgeon status: ${
|
|
16311
|
+
process.stderr.write(`Failed to load surgeon status: ${formatUnknownError10(error)}
|
|
15797
16312
|
`);
|
|
15798
16313
|
}
|
|
15799
16314
|
});
|
|
@@ -15807,7 +16322,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15807
16322
|
process.stdout.write(renderHistory(runs, limit));
|
|
15808
16323
|
} catch (error) {
|
|
15809
16324
|
process.exitCode = 1;
|
|
15810
|
-
process.stderr.write(`Failed to load surgeon history: ${
|
|
16325
|
+
process.stderr.write(`Failed to load surgeon history: ${formatUnknownError10(error)}
|
|
15811
16326
|
`);
|
|
15812
16327
|
}
|
|
15813
16328
|
});
|
|
@@ -15834,7 +16349,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15834
16349
|
);
|
|
15835
16350
|
} catch (error) {
|
|
15836
16351
|
process.exitCode = 1;
|
|
15837
|
-
process.stderr.write(`Failed to load surgeon backlog: ${
|
|
16352
|
+
process.stderr.write(`Failed to load surgeon backlog: ${formatUnknownError10(error)}
|
|
15838
16353
|
`);
|
|
15839
16354
|
}
|
|
15840
16355
|
});
|
|
@@ -15847,7 +16362,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15847
16362
|
process.stdout.write(renderActions(runId, actions));
|
|
15848
16363
|
} catch (error) {
|
|
15849
16364
|
process.exitCode = 1;
|
|
15850
|
-
process.stderr.write(`Failed to load surgeon actions: ${
|
|
16365
|
+
process.stderr.write(`Failed to load surgeon actions: ${formatUnknownError10(error)}
|
|
15851
16366
|
`);
|
|
15852
16367
|
}
|
|
15853
16368
|
});
|
|
@@ -15860,7 +16375,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15860
16375
|
process.stdout.write(renderProposals(runId, proposals));
|
|
15861
16376
|
} catch (error) {
|
|
15862
16377
|
process.exitCode = 1;
|
|
15863
|
-
process.stderr.write(`Failed to load surgeon proposals: ${
|
|
16378
|
+
process.stderr.write(`Failed to load surgeon proposals: ${formatUnknownError10(error)}
|
|
15864
16379
|
`);
|
|
15865
16380
|
}
|
|
15866
16381
|
});
|
|
@@ -15879,7 +16394,7 @@ function registerSurgeonCommand(program2) {
|
|
|
15879
16394
|
process.stdout.write(renderProposalReviewResult(result));
|
|
15880
16395
|
} catch (error) {
|
|
15881
16396
|
process.exitCode = 1;
|
|
15882
|
-
process.stderr.write(`Failed to review surgeon proposal: ${
|
|
16397
|
+
process.stderr.write(`Failed to review surgeon proposal: ${formatUnknownError10(error)}
|
|
15883
16398
|
`);
|
|
15884
16399
|
}
|
|
15885
16400
|
});
|
|
@@ -16004,7 +16519,7 @@ function aggregatePasses(passes) {
|
|
|
16004
16519
|
return order.map((passType) => aggregates.get(passType)).filter((value) => value !== void 0);
|
|
16005
16520
|
}
|
|
16006
16521
|
function formatPassAggregateLine(aggregate) {
|
|
16007
|
-
const metricParts = [`${aggregate.runs} ${
|
|
16522
|
+
const metricParts = [`${aggregate.runs} ${pluralize5(aggregate.runs, "pass")}`, `${aggregate.actionsTaken} ${pluralize5(aggregate.actionsTaken, "action")}`];
|
|
16008
16523
|
if (aggregate.entriesRetired > 0) {
|
|
16009
16524
|
metricParts.push(`${aggregate.entriesRetired} retired`);
|
|
16010
16525
|
}
|
|
@@ -16022,7 +16537,7 @@ function extractPassSummarySnippet(summary) {
|
|
|
16022
16537
|
const candidate = lines.find((line) => !line.endsWith(":") && !line.startsWith("- ")) ?? lines.find((line) => line.startsWith("- "))?.slice(2) ?? lines[0] ?? null;
|
|
16023
16538
|
return candidate ? truncateDisplayText(candidate, 100) : null;
|
|
16024
16539
|
}
|
|
16025
|
-
function
|
|
16540
|
+
function pluralize5(count, singular, plural) {
|
|
16026
16541
|
if (count === 1) {
|
|
16027
16542
|
return singular;
|
|
16028
16543
|
}
|
|
@@ -16588,7 +17103,7 @@ function formatElapsed(elapsedMs2) {
|
|
|
16588
17103
|
function formatUsd2(value) {
|
|
16589
17104
|
return `$${value.toFixed(4)}`;
|
|
16590
17105
|
}
|
|
16591
|
-
function
|
|
17106
|
+
function formatUnknownError10(error) {
|
|
16592
17107
|
return error instanceof Error ? error.message : String(error);
|
|
16593
17108
|
}
|
|
16594
17109
|
function normalizeOptionalBudget(value) {
|
|
@@ -16599,15 +17114,15 @@ function formatOptionalCount(value) {
|
|
|
16599
17114
|
}
|
|
16600
17115
|
function formatPassContextSummary(event) {
|
|
16601
17116
|
const activeEntryCount = formatOptionalCount(event.workingSetSize);
|
|
16602
|
-
const activeEntriesSummary = `${activeEntryCount} active ${
|
|
17117
|
+
const activeEntriesSummary = `${activeEntryCount} active ${pluralize5(activeEntryCount, "entry", "entries")}`;
|
|
16603
17118
|
if (event.passType === "proposal_resolution") {
|
|
16604
17119
|
const eligibleProposalCount = formatOptionalCount(event.eligibleProposalBacklogCount);
|
|
16605
|
-
return `${activeEntriesSummary} | Proposal backlog: ${eligibleProposalCount} eligible ${
|
|
17120
|
+
return `${activeEntriesSummary} | Proposal backlog: ${eligibleProposalCount} eligible ${pluralize5(eligibleProposalCount, "proposal")}`;
|
|
16606
17121
|
}
|
|
16607
17122
|
if (event.passType === "supersession") {
|
|
16608
17123
|
const claimKeyClusterCount = formatOptionalCount(event.supersessionClaimKeyCount);
|
|
16609
17124
|
const subjectClusterCount = formatOptionalCount(event.supersessionSubjectCount);
|
|
16610
|
-
return `${activeEntriesSummary} | Supersession remaining: ${claimKeyClusterCount} claim_key ${
|
|
17125
|
+
return `${activeEntriesSummary} | Supersession remaining: ${claimKeyClusterCount} claim_key ${pluralize5(claimKeyClusterCount, "cluster")}, ${subjectClusterCount} subject ${pluralize5(subjectClusterCount, "cluster")}`;
|
|
16611
17126
|
}
|
|
16612
17127
|
if (event.passType === "retirement") {
|
|
16613
17128
|
const actionableCount = formatOptionalCount(event.retirementAvailableActionableCount);
|
|
@@ -16693,7 +17208,7 @@ function registerTraceCommand(program2) {
|
|
|
16693
17208
|
process.stdout.write(options.json === true ? renderTraceJson(trace) : renderTrace(trace));
|
|
16694
17209
|
} catch (error) {
|
|
16695
17210
|
process.exitCode = 1;
|
|
16696
|
-
process.stderr.write(`Failed to load trace: ${
|
|
17211
|
+
process.stderr.write(`Failed to load trace: ${formatUnknownError11(error)}
|
|
16697
17212
|
`);
|
|
16698
17213
|
}
|
|
16699
17214
|
});
|
|
@@ -16851,7 +17366,7 @@ function truncate2(value, maxChars) {
|
|
|
16851
17366
|
}
|
|
16852
17367
|
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
16853
17368
|
}
|
|
16854
|
-
function
|
|
17369
|
+
function formatUnknownError11(error) {
|
|
16855
17370
|
return error instanceof Error ? error.message : String(error);
|
|
16856
17371
|
}
|
|
16857
17372
|
|