@ouro.bot/cli 0.1.0-alpha.730 → 0.1.0-alpha.732
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/README.md +8 -0
- package/changelog.json +18 -0
- package/dist/heart/daemon/cli-exec.js +38 -4
- package/dist/heart/daemon/cli-help.js +9 -4
- package/dist/heart/daemon/cli-parse.js +30 -6
- package/dist/heart/daemon/doctor.js +294 -28
- package/dist/heart/daemon/freshness.js +4 -2
- package/dist/heart/daemon/logs-prune.js +26 -4
- package/dist/heart/daemon/prunable-bundle.js +144 -0
- package/dist/mailroom/blob-store.js +102 -6
- package/dist/mailroom/cache-sync-cli.js +54 -0
- package/dist/mailroom/hosted-cache-sync.js +249 -0
- package/dist/mailroom/reader.js +59 -0
- package/dist/mailroom/search-cache.js +76 -6
- package/dist/repertoire/guardrails.js +674 -0
- package/dist/repertoire/tools-mail.js +5 -152
- package/package.json +1 -1
|
@@ -8,7 +8,6 @@ exports.renderCachedMessageSummary = renderCachedMessageSummary;
|
|
|
8
8
|
exports.mergeCachedMailSearchDocuments = mergeCachedMailSearchDocuments;
|
|
9
9
|
exports.searchSuccessfulImportArchives = searchSuccessfulImportArchives;
|
|
10
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
-
const node_crypto_1 = require("node:crypto");
|
|
12
11
|
const friends_1 = require("@ouro.bot/friends");
|
|
13
12
|
const file_store_1 = require("../mailroom/file-store");
|
|
14
13
|
const reader_1 = require("../mailroom/reader");
|
|
@@ -25,8 +24,8 @@ const credential_access_1 = require("./credential-access");
|
|
|
25
24
|
const background_operations_1 = require("../heart/background-operations");
|
|
26
25
|
const mail_import_discovery_1 = require("../heart/mail-import-discovery");
|
|
27
26
|
const identity_1 = require("../heart/identity");
|
|
28
|
-
const
|
|
29
|
-
const
|
|
27
|
+
const blob_store_1 = require("../mailroom/blob-store");
|
|
28
|
+
const hosted_cache_sync_1 = require("../mailroom/hosted-cache-sync");
|
|
30
29
|
function trustAllowsMailRead(ctx) {
|
|
31
30
|
const trustLevel = ctx?.context?.friend?.trustLevel;
|
|
32
31
|
const allowed = trustLevel === undefined || (0, friends_1.isTrustedLevel)(trustLevel);
|
|
@@ -300,24 +299,6 @@ function appendDelegatedSearchCoverage(body, input) {
|
|
|
300
299
|
].filter(Boolean).join(" "),
|
|
301
300
|
].join("\n\n");
|
|
302
301
|
}
|
|
303
|
-
async function mapWithConcurrency(items, concurrency, worker) {
|
|
304
|
-
/* v8 ignore next -- refresh callers pass only non-empty slices into the worker pool. @preserve */
|
|
305
|
-
if (items.length === 0)
|
|
306
|
-
return [];
|
|
307
|
-
const results = new Array(items.length);
|
|
308
|
-
let nextIndex = 0;
|
|
309
|
-
const workerLoop = async () => {
|
|
310
|
-
while (true) {
|
|
311
|
-
const current = nextIndex;
|
|
312
|
-
nextIndex += 1;
|
|
313
|
-
if (current >= items.length)
|
|
314
|
-
return;
|
|
315
|
-
results[current] = await worker(items[current], current);
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => workerLoop()));
|
|
319
|
-
return results;
|
|
320
|
-
}
|
|
321
302
|
function mailListFilters(input) {
|
|
322
303
|
return {
|
|
323
304
|
agentId: input.agentId,
|
|
@@ -338,28 +319,6 @@ function mailSearchCoverageKey(input) {
|
|
|
338
319
|
...(input.source ? { source: input.source } : {}),
|
|
339
320
|
};
|
|
340
321
|
}
|
|
341
|
-
function mailSearchCacheDocumentNeedsProjectionRefresh(document) {
|
|
342
|
-
if (document.textProjectionVersion === search_cache_1.MAIL_SEARCH_TEXT_PROJECTION_VERSION)
|
|
343
|
-
return false;
|
|
344
|
-
return document.textExcerpt.trim().length === 0;
|
|
345
|
-
}
|
|
346
|
-
function mailSearchCoverageSnapshot(records) {
|
|
347
|
-
const normalizedRecords = records
|
|
348
|
-
.map((record) => ({
|
|
349
|
-
id: record.id,
|
|
350
|
-
receivedAt: record.receivedAt,
|
|
351
|
-
placement: record.placement,
|
|
352
|
-
compartmentKind: record.compartmentKind,
|
|
353
|
-
source: record.source?.toLowerCase() ?? null,
|
|
354
|
-
}))
|
|
355
|
-
.sort((left, right) => left.id.localeCompare(right.id));
|
|
356
|
-
return {
|
|
357
|
-
visibleMessageCount: records.length,
|
|
358
|
-
messageIndexFingerprint: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(normalizedRecords)).digest("hex"),
|
|
359
|
-
...(oldestReceivedAt(records) ? { oldestReceivedAt: oldestReceivedAt(records) } : {}),
|
|
360
|
-
...(newestReceivedAt(records) ? { newestReceivedAt: newestReceivedAt(records) } : {}),
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
322
|
function mailSearchCoverageStalenessReason(record, currentSnapshot) {
|
|
364
323
|
if (!record)
|
|
365
324
|
return "hosted search index incomplete";
|
|
@@ -389,113 +348,6 @@ function mailSearchCoverageUnsearchableReason(record) {
|
|
|
389
348
|
const noun = unsearchableCount === 1 ? "message was" : "messages were";
|
|
390
349
|
return `${unsearchableCount} visible ${noun} not searchable because the index skipped missing/decryption-key mail`;
|
|
391
350
|
}
|
|
392
|
-
function newestReceivedAt(records) {
|
|
393
|
-
return records.reduce((newest, record) => {
|
|
394
|
-
if (!newest || Date.parse(record.receivedAt) > Date.parse(newest))
|
|
395
|
-
return record.receivedAt;
|
|
396
|
-
return newest;
|
|
397
|
-
}, undefined);
|
|
398
|
-
}
|
|
399
|
-
function oldestReceivedAt(records) {
|
|
400
|
-
return records.reduce((oldest, record) => {
|
|
401
|
-
if (!oldest || Date.parse(record.receivedAt) < Date.parse(oldest))
|
|
402
|
-
return record.receivedAt;
|
|
403
|
-
return oldest;
|
|
404
|
-
}, undefined);
|
|
405
|
-
}
|
|
406
|
-
async function refreshMailSearchIndex(input) {
|
|
407
|
-
const filters = mailListFilters(input);
|
|
408
|
-
const indexedRecords = await input.store.listMessageIndexRecords?.(filters);
|
|
409
|
-
const visibleRecords = indexedRecords ?? (await input.store.listMessages(filters)).map((message) => ({
|
|
410
|
-
schemaVersion: 1,
|
|
411
|
-
id: message.id,
|
|
412
|
-
agentId: message.agentId,
|
|
413
|
-
compartmentKind: message.compartmentKind,
|
|
414
|
-
placement: message.placement,
|
|
415
|
-
...(message.source ? { source: message.source } : {}),
|
|
416
|
-
receivedAt: message.receivedAt,
|
|
417
|
-
}));
|
|
418
|
-
const visibleIds = new Set(visibleRecords.map((record) => record.id));
|
|
419
|
-
const coverageSnapshot = mailSearchCoverageSnapshot(visibleRecords);
|
|
420
|
-
const cachedForScope = (0, search_cache_1.searchMailSearchCache)({
|
|
421
|
-
agentId: input.agentId,
|
|
422
|
-
placement: input.placement,
|
|
423
|
-
compartmentKind: input.scope,
|
|
424
|
-
source: input.source,
|
|
425
|
-
}, input.cacheOptions);
|
|
426
|
-
const cachedVisibleIds = new Set(cachedForScope
|
|
427
|
-
.filter((document) => visibleIds.has(document.messageId))
|
|
428
|
-
.filter((document) => !mailSearchCacheDocumentNeedsProjectionRefresh(document))
|
|
429
|
-
.map((document) => document.messageId));
|
|
430
|
-
const idsToFetch = visibleRecords
|
|
431
|
-
.filter((record) => !cachedVisibleIds.has(record.id))
|
|
432
|
-
.map((record) => record.id);
|
|
433
|
-
const failures = [];
|
|
434
|
-
let fetchedCount = 0;
|
|
435
|
-
let skippedCount = 0;
|
|
436
|
-
for (let start = 0; start < idsToFetch.length; start += MAIL_SEARCH_INDEX_BATCH_SIZE) {
|
|
437
|
-
const batchIds = idsToFetch.slice(start, start + MAIL_SEARCH_INDEX_BATCH_SIZE);
|
|
438
|
-
const fetchedMessages = await mapWithConcurrency(batchIds, MAIL_SEARCH_INDEX_FETCH_CONCURRENCY, async (id) => {
|
|
439
|
-
try {
|
|
440
|
-
const message = input.store.getIndexedMessageById
|
|
441
|
-
? await input.store.getIndexedMessageById(id)
|
|
442
|
-
: await input.store.getMessage(id);
|
|
443
|
-
return { id, message, error: message ? null : "indexed message was not retrievable" };
|
|
444
|
-
}
|
|
445
|
-
catch (error) {
|
|
446
|
-
return { id, message: null, error: error instanceof Error ? error.message : String(error) };
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
failures.push(...fetchedMessages.filter((entry) => entry.error !== null));
|
|
450
|
-
const fetchedStored = fetchedMessages
|
|
451
|
-
.map((entry) => entry.message)
|
|
452
|
-
.filter((message) => message !== null);
|
|
453
|
-
const result = decryptVisibleMessages(fetchedStored, input.privateKeys);
|
|
454
|
-
cacheDecryptedMessages(result.decrypted, input.cacheOptions);
|
|
455
|
-
fetchedCount += fetchedStored.length;
|
|
456
|
-
skippedCount += result.skipped.length;
|
|
457
|
-
(0, runtime_1.emitNervesEvent)({
|
|
458
|
-
component: "repertoire",
|
|
459
|
-
event: "repertoire.mail_search_index_refresh_progress",
|
|
460
|
-
message: "mail search index refresh cached a batch",
|
|
461
|
-
meta: {
|
|
462
|
-
agentId: input.agentId,
|
|
463
|
-
fetched: fetchedCount,
|
|
464
|
-
totalToFetch: idsToFetch.length,
|
|
465
|
-
alreadyCached: cachedVisibleIds.size,
|
|
466
|
-
failures: failures.length,
|
|
467
|
-
},
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
const refreshedCachedForScope = (0, search_cache_1.searchMailSearchCache)({
|
|
471
|
-
agentId: input.agentId,
|
|
472
|
-
placement: input.placement,
|
|
473
|
-
compartmentKind: input.scope,
|
|
474
|
-
source: input.source,
|
|
475
|
-
}, input.cacheOptions).filter((document) => visibleIds.has(document.messageId));
|
|
476
|
-
if (failures.length > 0) {
|
|
477
|
-
const sample = failures.slice(0, 3).map((entry) => `${entry.id}: ${entry.error}`).join("; ");
|
|
478
|
-
throw new Error(`mail search index refresh incomplete after fetching ${fetchedCount}/${idsToFetch.length} missing message(s); ${failures.length} fetch failed. first failure(s): ${sample}`);
|
|
479
|
-
}
|
|
480
|
-
const coverage = (0, search_cache_1.writeMailSearchCoverageRecord)({
|
|
481
|
-
schemaVersion: 1,
|
|
482
|
-
...mailSearchCoverageKey(input),
|
|
483
|
-
indexedAt: new Date().toISOString(),
|
|
484
|
-
visibleMessageCount: visibleRecords.length,
|
|
485
|
-
cachedMessageCount: refreshedCachedForScope.length,
|
|
486
|
-
decryptableMessageCount: refreshedCachedForScope.length,
|
|
487
|
-
skippedMessageCount: skippedCount,
|
|
488
|
-
messageIndexFingerprint: coverageSnapshot.messageIndexFingerprint,
|
|
489
|
-
textProjectionVersion: search_cache_1.MAIL_SEARCH_TEXT_PROJECTION_VERSION,
|
|
490
|
-
...(coverageSnapshot.oldestReceivedAt ? { oldestReceivedAt: coverageSnapshot.oldestReceivedAt } : {}),
|
|
491
|
-
...(coverageSnapshot.newestReceivedAt ? { newestReceivedAt: coverageSnapshot.newestReceivedAt } : {}),
|
|
492
|
-
}, input.cacheOptions);
|
|
493
|
-
return {
|
|
494
|
-
coverage,
|
|
495
|
-
fetched: fetchedCount,
|
|
496
|
-
alreadyCached: cachedVisibleIds.size,
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
351
|
function accessProvenance(message) {
|
|
500
352
|
const provenance = (0, core_1.describeMailProvenance)(message);
|
|
501
353
|
return {
|
|
@@ -1390,7 +1242,7 @@ exports.mailToolDefinitions = [
|
|
|
1390
1242
|
}
|
|
1391
1243
|
if (hostedDelegatedSearch && storedCoverageRecord) {
|
|
1392
1244
|
if (currentIndexRecordsLoaded) {
|
|
1393
|
-
currentCoverageSnapshot =
|
|
1245
|
+
currentCoverageSnapshot = (0, blob_store_1.canonicalMailIndexRecordSnapshot)(currentIndexRecords);
|
|
1394
1246
|
}
|
|
1395
1247
|
else if (!coverageValidationError) {
|
|
1396
1248
|
coverageValidationError = "hosted store does not expose message index records";
|
|
@@ -1575,8 +1427,9 @@ exports.mailToolDefinitions = [
|
|
|
1575
1427
|
: requestedScope ?? (args.source ? "delegated" : (familyOrAgentSelf(ctx) ? undefined : "native"));
|
|
1576
1428
|
try {
|
|
1577
1429
|
const cacheOptions = mailSearchCacheOptionsForStore(resolved.store);
|
|
1578
|
-
const refreshed = await
|
|
1430
|
+
const refreshed = await (0, hosted_cache_sync_1.syncHostedMailSearchCache)({
|
|
1579
1431
|
agentId: resolved.agentName,
|
|
1432
|
+
mode: "scoped-upsert",
|
|
1580
1433
|
store: resolved.store,
|
|
1581
1434
|
privateKeys: resolved.config.privateKeys,
|
|
1582
1435
|
storeKind: resolved.storeKind,
|