@ouro.bot/cli 0.1.0-alpha.484 → 0.1.0-alpha.486
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.json +17 -0
- package/dist/heart/active-work.js +89 -3
- package/dist/heart/background-operations.js +26 -3
- package/dist/heart/daemon/cli-exec.js +171 -9
- package/dist/heart/mail-import-discovery.js +37 -2
- package/dist/heart/providers/azure.js +1 -1
- package/dist/heart/providers/github-copilot.js +1 -1
- package/dist/heart/providers/openai-codex.js +1 -1
- package/dist/heart/session-events.js +40 -3
- package/dist/heart/streaming.js +13 -2
- package/dist/mailroom/blob-store.js +16 -10
- package/dist/mailroom/core.js +1 -1
- package/dist/mailroom/file-store.js +35 -9
- package/dist/mailroom/mbox-import.js +41 -0
- package/dist/mailroom/reader.js +22 -0
- package/dist/mailroom/search-cache.js +182 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mind/context.js +36 -4
- package/dist/mind/friends/resolver.js +16 -1
- package/dist/nerves/coverage/file-completeness.js +4 -0
- package/dist/repertoire/tools-mail.js +453 -68
- package/dist/senses/bluebubbles/inbound-log.js +13 -0
- package/dist/senses/bluebubbles/index.js +394 -236
- package/dist/senses/bluebubbles/processed-log.js +111 -0
- package/dist/senses/inner-dialog-worker.js +38 -2
- package/dist/senses/mail.js +19 -3
- package/dist/senses/trust-gate.js +96 -1
- package/package.json +1 -1
package/dist/heart/streaming.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SettleStreamer = exports.SettleParser = void 0;
|
|
3
|
+
exports.RESPONSES_FUNCTION_CALL_OUTPUT_CAP = exports.SettleStreamer = exports.SettleParser = void 0;
|
|
4
|
+
exports.truncateResponsesFunctionCallOutput = truncateResponsesFunctionCallOutput;
|
|
4
5
|
exports.toResponsesInput = toResponsesInput;
|
|
5
6
|
exports.toResponsesTools = toResponsesTools;
|
|
6
7
|
exports.streamChatCompletion = streamChatCompletion;
|
|
@@ -112,6 +113,16 @@ class SettleStreamer {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
exports.SettleStreamer = SettleStreamer;
|
|
116
|
+
exports.RESPONSES_FUNCTION_CALL_OUTPUT_CAP = 200_000;
|
|
117
|
+
function truncateResponsesFunctionCallOutput(output, maxChars = exports.RESPONSES_FUNCTION_CALL_OUTPUT_CAP) {
|
|
118
|
+
if (output.length <= maxChars)
|
|
119
|
+
return output;
|
|
120
|
+
const marker = `\n\n[truncated — function_call_output exceeded ${maxChars} chars; original length ${output.length} chars]\n\n`;
|
|
121
|
+
const remainingBudget = Math.max(0, maxChars - marker.length);
|
|
122
|
+
const headLength = Math.ceil(remainingBudget * 0.75);
|
|
123
|
+
const tailLength = Math.max(0, remainingBudget - headLength);
|
|
124
|
+
return `${output.slice(0, headLength)}${marker}${output.slice(-tailLength)}`;
|
|
125
|
+
}
|
|
115
126
|
function toResponsesUserContent(content) {
|
|
116
127
|
if (typeof content === "string") {
|
|
117
128
|
return content;
|
|
@@ -217,7 +228,7 @@ function toResponsesInput(messages) {
|
|
|
217
228
|
input.push({
|
|
218
229
|
type: "function_call_output",
|
|
219
230
|
call_id: t.tool_call_id,
|
|
220
|
-
output: typeof t.content === "string" ? t.content : "",
|
|
231
|
+
output: truncateResponsesFunctionCallOutput(typeof t.content === "string" ? t.content : ""),
|
|
221
232
|
});
|
|
222
233
|
continue;
|
|
223
234
|
}
|
|
@@ -4,6 +4,7 @@ exports.AzureBlobMailroomStore = void 0;
|
|
|
4
4
|
exports.decryptBlobMessages = decryptBlobMessages;
|
|
5
5
|
const runtime_1 = require("../nerves/runtime");
|
|
6
6
|
const core_1 = require("./core");
|
|
7
|
+
const search_cache_1 = require("./search-cache");
|
|
7
8
|
const MESSAGE_INDEX_PREFIX = "message-index";
|
|
8
9
|
const MESSAGE_INDEX_SORT_MAX_MS = 9_999_999_999_999;
|
|
9
10
|
const MESSAGE_INDEX_SORT_WIDTH = 13;
|
|
@@ -22,6 +23,9 @@ function compareCandidatesNewestFirst(left, right) {
|
|
|
22
23
|
function blobText(value) {
|
|
23
24
|
return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
24
25
|
}
|
|
26
|
+
function applyOptionalLimit(items, limit) {
|
|
27
|
+
return typeof limit === "number" ? items.slice(0, limit) : items;
|
|
28
|
+
}
|
|
25
29
|
function positiveInteger(value, fallback) {
|
|
26
30
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
27
31
|
return fallback;
|
|
@@ -248,7 +252,6 @@ class AzureBlobMailroomStore {
|
|
|
248
252
|
messageBlobNames.push(item.name);
|
|
249
253
|
}
|
|
250
254
|
const matches = [];
|
|
251
|
-
const limit = filters.limit ?? 20;
|
|
252
255
|
let nextIndex = 0;
|
|
253
256
|
const worker = async () => {
|
|
254
257
|
while (nextIndex < messageBlobNames.length) {
|
|
@@ -259,12 +262,12 @@ class AzureBlobMailroomStore {
|
|
|
259
262
|
continue;
|
|
260
263
|
matches.push(message);
|
|
261
264
|
matches.sort(compareNewestFirst);
|
|
262
|
-
if (matches.length > limit)
|
|
263
|
-
matches.length = limit;
|
|
265
|
+
if (typeof filters.limit === "number" && matches.length > filters.limit)
|
|
266
|
+
matches.length = filters.limit;
|
|
264
267
|
}
|
|
265
268
|
};
|
|
266
269
|
await Promise.all(Array.from({ length: Math.min(MESSAGE_LIST_SCAN_CONCURRENCY, Math.max(messageBlobNames.length, 1)) }, async () => worker()));
|
|
267
|
-
return matches.sort(compareNewestFirst)
|
|
270
|
+
return applyOptionalLimit(matches.sort(compareNewestFirst), filters.limit);
|
|
268
271
|
}
|
|
269
272
|
async listMessagesFromIndexes(filters) {
|
|
270
273
|
const messageIds = [];
|
|
@@ -275,18 +278,18 @@ class AzureBlobMailroomStore {
|
|
|
275
278
|
if (!parsed || !messageMatchesFilters(parsed, filters))
|
|
276
279
|
continue;
|
|
277
280
|
messageIds.push(parsed.id);
|
|
278
|
-
if (messageIds.length >=
|
|
281
|
+
if (typeof filters.limit === "number" && messageIds.length >= filters.limit)
|
|
279
282
|
break;
|
|
280
283
|
}
|
|
281
284
|
if (!sawIndex)
|
|
282
285
|
return null;
|
|
283
|
-
|
|
286
|
+
const messages = (await mapWithConcurrency(messageIds, this.messageFetchConcurrency, async (id) => {
|
|
284
287
|
return downloadJson(this.messageBlob(id), this.blobOperationTimeoutMs);
|
|
285
288
|
}))
|
|
286
289
|
.filter((message) => message !== null)
|
|
287
290
|
.filter((message) => messageMatchesFilters(message, filters))
|
|
288
|
-
.sort(compareNewestFirst)
|
|
289
|
-
|
|
291
|
+
.sort(compareNewestFirst);
|
|
292
|
+
return applyOptionalLimit(messages, filters.limit);
|
|
290
293
|
}
|
|
291
294
|
async backfillMessageIndexes(agentId, onProgress) {
|
|
292
295
|
await this.ensureContainer();
|
|
@@ -335,7 +338,7 @@ class AzureBlobMailroomStore {
|
|
|
335
338
|
}
|
|
336
339
|
async putRawMessage(input) {
|
|
337
340
|
await this.ensureContainer();
|
|
338
|
-
const { message, rawPayload, candidate } = await (0, core_1.buildStoredMailMessage)(input);
|
|
341
|
+
const { message, rawPayload, privateEnvelope, candidate } = await (0, core_1.buildStoredMailMessage)(input);
|
|
339
342
|
const messageBlob = this.messageBlob(message.id);
|
|
340
343
|
let existing = null;
|
|
341
344
|
try {
|
|
@@ -359,6 +362,7 @@ class AzureBlobMailroomStore {
|
|
|
359
362
|
throw error;
|
|
360
363
|
}
|
|
361
364
|
if (existing) {
|
|
365
|
+
(0, search_cache_1.upsertMailSearchCacheDocument)(existing, privateEnvelope);
|
|
362
366
|
await this.putMessageIndex(existing);
|
|
363
367
|
(0, runtime_1.emitNervesEvent)({
|
|
364
368
|
component: "senses",
|
|
@@ -371,6 +375,7 @@ class AzureBlobMailroomStore {
|
|
|
371
375
|
await this.rawBlob(message.rawObject).uploadData(blobText(rawPayload));
|
|
372
376
|
await this.messageBlob(message.id).uploadData(blobText(message));
|
|
373
377
|
await this.putMessageIndex(message);
|
|
378
|
+
(0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope);
|
|
374
379
|
if (candidate) {
|
|
375
380
|
await this.candidateBlob(candidate.id).uploadData(blobText(candidate));
|
|
376
381
|
}
|
|
@@ -426,6 +431,7 @@ class AzureBlobMailroomStore {
|
|
|
426
431
|
await blob.uploadData(blobText(updated));
|
|
427
432
|
await this.removeMessageIndex(message);
|
|
428
433
|
await this.putMessageIndex(updated);
|
|
434
|
+
(0, search_cache_1.syncMailSearchCacheMetadata)(updated);
|
|
429
435
|
(0, runtime_1.emitNervesEvent)({
|
|
430
436
|
component: "senses",
|
|
431
437
|
event: "senses.mail_blob_store_message_placement_updated",
|
|
@@ -578,7 +584,7 @@ class AzureBlobMailroomStore {
|
|
|
578
584
|
async listAccessLog(agentId) {
|
|
579
585
|
await this.ensureContainer();
|
|
580
586
|
const entries = await downloadJson(this.accessLogBlob(agentId), this.blobOperationTimeoutMs);
|
|
581
|
-
const safeEntries = Array.isArray(entries) ? entries : [];
|
|
587
|
+
const safeEntries = (Array.isArray(entries) ? entries : []);
|
|
582
588
|
(0, runtime_1.emitNervesEvent)({
|
|
583
589
|
component: "senses",
|
|
584
590
|
event: "senses.mail_blob_access_log_listed",
|
package/dist/mailroom/core.js
CHANGED
|
@@ -477,7 +477,7 @@ async function buildStoredMailMessage(input) {
|
|
|
477
477
|
message: "stored mail message envelope built",
|
|
478
478
|
meta: { id, agentId: message.agentId, placement, compartmentKind: message.compartmentKind, candidate: candidate !== undefined },
|
|
479
479
|
});
|
|
480
|
-
return { message, rawPayload, ...(candidate ? { candidate } : {}) };
|
|
480
|
+
return { message, rawPayload, privateEnvelope, ...(candidate ? { candidate } : {}) };
|
|
481
481
|
}
|
|
482
482
|
function decryptStoredMailMessage(message, privateKeys) {
|
|
483
483
|
const privateKey = privateKeys[message.privateEnvelope.keyId];
|
|
@@ -40,9 +40,13 @@ const fs = __importStar(require("node:fs"));
|
|
|
40
40
|
const path = __importStar(require("node:path"));
|
|
41
41
|
const runtime_1 = require("../nerves/runtime");
|
|
42
42
|
const core_1 = require("./core");
|
|
43
|
+
const search_cache_1 = require("./search-cache");
|
|
43
44
|
function ensureDir(dir) {
|
|
44
45
|
fs.mkdirSync(dir, { recursive: true });
|
|
45
46
|
}
|
|
47
|
+
function applyOptionalLimit(items, limit) {
|
|
48
|
+
return typeof limit === "number" ? items.slice(0, limit) : items;
|
|
49
|
+
}
|
|
46
50
|
function readJson(filePath) {
|
|
47
51
|
try {
|
|
48
52
|
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
@@ -122,9 +126,10 @@ class FileMailroomStore {
|
|
|
122
126
|
return path.join(this.logsDir, `${agentId}.jsonl`);
|
|
123
127
|
}
|
|
124
128
|
async putRawMessage(input) {
|
|
125
|
-
const { message, rawPayload, candidate } = await (0, core_1.buildStoredMailMessage)(input);
|
|
129
|
+
const { message, rawPayload, privateEnvelope, candidate } = await (0, core_1.buildStoredMailMessage)(input);
|
|
126
130
|
const existing = readJson(this.messagePath(message.id));
|
|
127
131
|
if (existing) {
|
|
132
|
+
(0, search_cache_1.upsertMailSearchCacheDocument)(existing, privateEnvelope);
|
|
128
133
|
(0, runtime_1.emitNervesEvent)({
|
|
129
134
|
component: "senses",
|
|
130
135
|
event: "senses.mail_store_dedupe",
|
|
@@ -135,6 +140,7 @@ class FileMailroomStore {
|
|
|
135
140
|
}
|
|
136
141
|
writeJson(this.rawPath(message.rawObject), rawPayload);
|
|
137
142
|
writeJson(this.messagePath(message.id), message);
|
|
143
|
+
(0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope);
|
|
138
144
|
if (candidate) {
|
|
139
145
|
writeJson(this.candidatePath(candidate.id), candidate);
|
|
140
146
|
}
|
|
@@ -165,15 +171,15 @@ class FileMailroomStore {
|
|
|
165
171
|
.filter((message) => filters.placement ? message.placement === filters.placement : true)
|
|
166
172
|
.filter((message) => filters.compartmentKind ? message.compartmentKind === filters.compartmentKind : true)
|
|
167
173
|
.filter((message) => sourceMatchesFilter(message.source, filters.source))
|
|
168
|
-
.sort(compareNewestFirst)
|
|
169
|
-
|
|
174
|
+
.sort(compareNewestFirst);
|
|
175
|
+
const limited = applyOptionalLimit(messages, filters.limit);
|
|
170
176
|
(0, runtime_1.emitNervesEvent)({
|
|
171
177
|
component: "senses",
|
|
172
178
|
event: "senses.mail_store_messages_listed",
|
|
173
179
|
message: "mailroom store listed messages",
|
|
174
|
-
meta: { agentId: filters.agentId, count:
|
|
180
|
+
meta: { agentId: filters.agentId, count: limited.length },
|
|
175
181
|
});
|
|
176
|
-
return
|
|
182
|
+
return limited;
|
|
177
183
|
}
|
|
178
184
|
async updateMessagePlacement(id, placement) {
|
|
179
185
|
const message = readJson(this.messagePath(id));
|
|
@@ -188,6 +194,7 @@ class FileMailroomStore {
|
|
|
188
194
|
}
|
|
189
195
|
const updated = { ...message, placement };
|
|
190
196
|
writeJson(this.messagePath(id), updated);
|
|
197
|
+
(0, search_cache_1.syncMailSearchCacheMetadata)(updated);
|
|
191
198
|
(0, runtime_1.emitNervesEvent)({
|
|
192
199
|
component: "senses",
|
|
193
200
|
event: "senses.mail_store_message_placement_updated",
|
|
@@ -339,15 +346,34 @@ class FileMailroomStore {
|
|
|
339
346
|
});
|
|
340
347
|
return [];
|
|
341
348
|
}
|
|
342
|
-
const
|
|
349
|
+
const lines = fs.readFileSync(filePath, "utf-8")
|
|
343
350
|
.split(/\r?\n/)
|
|
344
|
-
.filter(Boolean)
|
|
345
|
-
|
|
351
|
+
.filter(Boolean);
|
|
352
|
+
const entries = [];
|
|
353
|
+
let malformedEntriesSkipped = 0;
|
|
354
|
+
for (const line of lines) {
|
|
355
|
+
try {
|
|
356
|
+
entries.push(JSON.parse(line));
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
malformedEntriesSkipped += 1;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (malformedEntriesSkipped > 0) {
|
|
363
|
+
entries.malformedEntriesSkipped = malformedEntriesSkipped;
|
|
364
|
+
(0, runtime_1.emitNervesEvent)({
|
|
365
|
+
level: "warn",
|
|
366
|
+
component: "senses",
|
|
367
|
+
event: "senses.mail_access_log_malformed_lines_skipped",
|
|
368
|
+
message: "skipped malformed file-backed mail access log lines",
|
|
369
|
+
meta: { agentId, malformedEntriesSkipped },
|
|
370
|
+
});
|
|
371
|
+
}
|
|
346
372
|
(0, runtime_1.emitNervesEvent)({
|
|
347
373
|
component: "senses",
|
|
348
374
|
event: "senses.mail_access_log_listed",
|
|
349
375
|
message: "mail access log listed",
|
|
350
|
-
meta: { agentId, count: entries.length },
|
|
376
|
+
meta: { agentId, count: entries.length, malformedEntriesSkipped },
|
|
351
377
|
});
|
|
352
378
|
return entries;
|
|
353
379
|
}
|
|
@@ -36,9 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.splitMboxMessages = splitMboxMessages;
|
|
37
37
|
exports.importMboxToStore = importMboxToStore;
|
|
38
38
|
exports.importMboxFileToStore = importMboxFileToStore;
|
|
39
|
+
exports.cacheMatchingMailSearchDocumentsFromMboxFile = cacheMatchingMailSearchDocumentsFromMboxFile;
|
|
39
40
|
const fs = __importStar(require("node:fs"));
|
|
40
41
|
const runtime_1 = require("../nerves/runtime");
|
|
41
42
|
const core_1 = require("./core");
|
|
43
|
+
const search_cache_1 = require("./search-cache");
|
|
44
|
+
const search_relevance_1 = require("./search-relevance");
|
|
42
45
|
function splitMboxMessages(rawMbox) {
|
|
43
46
|
const text = rawMbox.toString("utf-8");
|
|
44
47
|
const separators = [...text.matchAll(/^From [^\r\n]*(?:\r?\n)/gm)];
|
|
@@ -208,6 +211,11 @@ function extractHeaderDate(rawMessage, headerName) {
|
|
|
208
211
|
const parsed = new Date(value);
|
|
209
212
|
return Number.isFinite(parsed.getTime()) ? parsed : undefined;
|
|
210
213
|
}
|
|
214
|
+
function normalizeSearchTerms(queryTerms) {
|
|
215
|
+
return queryTerms
|
|
216
|
+
.map((term) => term.trim().toLowerCase())
|
|
217
|
+
.filter((term) => term.length > 0);
|
|
218
|
+
}
|
|
211
219
|
function historicalImportClassification(resolvedPlacement, sourceGrant) {
|
|
212
220
|
return {
|
|
213
221
|
placement: resolvedPlacement,
|
|
@@ -339,3 +347,36 @@ async function importMboxFileToStore(input) {
|
|
|
339
347
|
onProgress: input.onProgress,
|
|
340
348
|
});
|
|
341
349
|
}
|
|
350
|
+
async function cacheMatchingMailSearchDocumentsFromMboxFile(input) {
|
|
351
|
+
const target = resolveImportTarget(input);
|
|
352
|
+
const queryTerms = normalizeSearchTerms(input.queryTerms);
|
|
353
|
+
if (queryTerms.length === 0 || input.limit <= 0)
|
|
354
|
+
return [];
|
|
355
|
+
const matches = [];
|
|
356
|
+
for await (const rawMessage of streamMboxMessagesFromFile(input.filePath)) {
|
|
357
|
+
const parsedMessage = parseMboxMessage(rawMessage, target.sourceGrant);
|
|
358
|
+
const { message, privateEnvelope } = await (0, core_1.buildStoredMailMessage)({
|
|
359
|
+
resolved: target.resolved,
|
|
360
|
+
envelope: parsedMessage.envelope,
|
|
361
|
+
rawMime: parsedMessage.rawMessage,
|
|
362
|
+
receivedAt: parsedMessage.messageDate,
|
|
363
|
+
ingest: {
|
|
364
|
+
schemaVersion: 1,
|
|
365
|
+
kind: "mbox-import",
|
|
366
|
+
attentionSuppressed: true,
|
|
367
|
+
},
|
|
368
|
+
classification: historicalImportClassification(target.resolved.defaultPlacement, target.sourceGrant),
|
|
369
|
+
});
|
|
370
|
+
const document = (0, search_cache_1.buildMailSearchCacheDocument)(message, privateEnvelope);
|
|
371
|
+
if (!queryTerms.some((term) => document.searchText.includes(term)))
|
|
372
|
+
continue;
|
|
373
|
+
(0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope);
|
|
374
|
+
matches.push(document);
|
|
375
|
+
if (matches.length >= input.limit)
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
return matches
|
|
379
|
+
.map((document) => ({ document, relevance: (0, search_relevance_1.scoreMailSearchDocument)(document, queryTerms) }))
|
|
380
|
+
.sort(search_relevance_1.compareByRelevanceThenRecency)
|
|
381
|
+
.map((entry) => entry.document);
|
|
382
|
+
}
|
package/dist/mailroom/reader.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.parseMailroomConfig = parseMailroomConfig;
|
|
37
37
|
exports.readMailroomRegistry = readMailroomRegistry;
|
|
38
|
+
exports.writeMailroomRegistry = writeMailroomRegistry;
|
|
38
39
|
exports.resolveMailroomReader = resolveMailroomReader;
|
|
39
40
|
const fs = __importStar(require("node:fs"));
|
|
40
41
|
const path = __importStar(require("node:path"));
|
|
@@ -145,6 +146,27 @@ async function readMailroomRegistry(config) {
|
|
|
145
146
|
}
|
|
146
147
|
return JSON.parse((await blobClient.downloadToBuffer()).toString("utf-8"));
|
|
147
148
|
}
|
|
149
|
+
async function writeMailroomRegistry(config, registry) {
|
|
150
|
+
const serialized = `${JSON.stringify(registry, null, 2)}\n`;
|
|
151
|
+
if (config.registryPath) {
|
|
152
|
+
fs.mkdirSync(path.dirname(config.registryPath), { recursive: true });
|
|
153
|
+
fs.writeFileSync(config.registryPath, serialized, "utf-8");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const registryAzureAccountUrl = config.registryAzureAccountUrl ?? config.azureAccountUrl;
|
|
157
|
+
const registryContainer = config.registryContainer ?? config.azureContainer ?? "mailroom";
|
|
158
|
+
const registryBlob = config.registryBlob;
|
|
159
|
+
if (!registryAzureAccountUrl || !registryBlob) {
|
|
160
|
+
throw new Error("mailroom config is missing registryPath or hosted registry coordinates");
|
|
161
|
+
}
|
|
162
|
+
const serviceClient = new storage_blob_1.BlobServiceClient(registryAzureAccountUrl, createBlobCredential(config));
|
|
163
|
+
const blobClient = serviceClient.getContainerClient(registryContainer).getBlockBlobClient(registryBlob);
|
|
164
|
+
await blobClient.upload(serialized, Buffer.byteLength(serialized), {
|
|
165
|
+
blobHTTPHeaders: {
|
|
166
|
+
blobContentType: "application/json; charset=utf-8",
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
148
170
|
function resolveMailroomReader(agentName = (0, identity_2.getAgentName)()) {
|
|
149
171
|
const runtime = (0, runtime_credentials_1.readRuntimeCredentialConfig)(agentName);
|
|
150
172
|
if (!runtime.ok) {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.buildMailSearchCacheDocument = buildMailSearchCacheDocument;
|
|
37
|
+
exports.upsertMailSearchCacheDocument = upsertMailSearchCacheDocument;
|
|
38
|
+
exports.syncMailSearchCacheMetadata = syncMailSearchCacheMetadata;
|
|
39
|
+
exports.searchMailSearchCache = searchMailSearchCache;
|
|
40
|
+
exports.resetMailSearchCacheForTests = resetMailSearchCacheForTests;
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const identity_1 = require("../heart/identity");
|
|
44
|
+
const runtime_1 = require("../nerves/runtime");
|
|
45
|
+
const search_relevance_1 = require("./search-relevance");
|
|
46
|
+
const SEARCH_TEXT_EXCERPT_LIMIT = 16_384;
|
|
47
|
+
const cacheStates = new Map();
|
|
48
|
+
function cacheDir(agentId) {
|
|
49
|
+
return path.join((0, identity_1.getAgentRoot)(agentId), "state", "mail-search");
|
|
50
|
+
}
|
|
51
|
+
function cachePath(agentId, messageId) {
|
|
52
|
+
return path.join(cacheDir(agentId), `${messageId}.json`);
|
|
53
|
+
}
|
|
54
|
+
function normalizeSearchText(privateEnvelope) {
|
|
55
|
+
return [
|
|
56
|
+
privateEnvelope.subject,
|
|
57
|
+
privateEnvelope.snippet,
|
|
58
|
+
privateEnvelope.text.slice(0, SEARCH_TEXT_EXCERPT_LIMIT),
|
|
59
|
+
privateEnvelope.from.join(" "),
|
|
60
|
+
].join("\n").toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
function readJsonDocument(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function cacheState(agentId) {
|
|
71
|
+
const key = `${agentId}:${cacheDir(agentId)}`;
|
|
72
|
+
let state = cacheStates.get(key);
|
|
73
|
+
if (state)
|
|
74
|
+
return state;
|
|
75
|
+
state = { loaded: false, docs: new Map() };
|
|
76
|
+
cacheStates.set(key, state);
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
function loadCache(agentId) {
|
|
80
|
+
const state = cacheState(agentId);
|
|
81
|
+
if (state.loaded)
|
|
82
|
+
return state.docs;
|
|
83
|
+
state.loaded = true;
|
|
84
|
+
const dir = cacheDir(agentId);
|
|
85
|
+
if (!fs.existsSync(dir))
|
|
86
|
+
return state.docs;
|
|
87
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
88
|
+
if (!entry.endsWith(".json"))
|
|
89
|
+
continue;
|
|
90
|
+
const document = readJsonDocument(path.join(dir, entry));
|
|
91
|
+
if (!document || document.agentId !== agentId)
|
|
92
|
+
continue;
|
|
93
|
+
state.docs.set(document.messageId, document);
|
|
94
|
+
}
|
|
95
|
+
return state.docs;
|
|
96
|
+
}
|
|
97
|
+
function buildMailSearchCacheDocument(message, privateEnvelope) {
|
|
98
|
+
return {
|
|
99
|
+
schemaVersion: 1,
|
|
100
|
+
messageId: message.id,
|
|
101
|
+
agentId: message.agentId,
|
|
102
|
+
receivedAt: message.receivedAt,
|
|
103
|
+
placement: message.placement,
|
|
104
|
+
compartmentKind: message.compartmentKind,
|
|
105
|
+
...(message.ownerEmail ? { ownerEmail: message.ownerEmail } : {}),
|
|
106
|
+
...(message.source ? { source: message.source } : {}),
|
|
107
|
+
from: [...privateEnvelope.from],
|
|
108
|
+
subject: privateEnvelope.subject,
|
|
109
|
+
snippet: privateEnvelope.snippet,
|
|
110
|
+
textExcerpt: privateEnvelope.text.slice(0, SEARCH_TEXT_EXCERPT_LIMIT),
|
|
111
|
+
untrustedContentWarning: privateEnvelope.untrustedContentWarning,
|
|
112
|
+
searchText: normalizeSearchText(privateEnvelope),
|
|
113
|
+
attachmentCount: privateEnvelope.attachments.length,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function upsertMailSearchCacheDocument(message, privateEnvelope) {
|
|
117
|
+
const document = buildMailSearchCacheDocument(message, privateEnvelope);
|
|
118
|
+
const dir = cacheDir(message.agentId);
|
|
119
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
120
|
+
fs.writeFileSync(cachePath(message.agentId, message.id), `${JSON.stringify(document)}\n`, "utf-8");
|
|
121
|
+
const docs = loadCache(message.agentId);
|
|
122
|
+
docs.set(document.messageId, document);
|
|
123
|
+
(0, runtime_1.emitNervesEvent)({
|
|
124
|
+
component: "senses",
|
|
125
|
+
event: "senses.mail_search_cache_upserted",
|
|
126
|
+
message: "mail search cache entry written",
|
|
127
|
+
meta: {
|
|
128
|
+
agentId: message.agentId,
|
|
129
|
+
messageId: document.messageId,
|
|
130
|
+
placement: document.placement,
|
|
131
|
+
compartmentKind: document.compartmentKind,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return document;
|
|
135
|
+
}
|
|
136
|
+
function syncMailSearchCacheMetadata(message) {
|
|
137
|
+
const existing = readJsonDocument(cachePath(message.agentId, message.id));
|
|
138
|
+
if (!existing)
|
|
139
|
+
return;
|
|
140
|
+
const updated = {
|
|
141
|
+
...existing,
|
|
142
|
+
receivedAt: message.receivedAt,
|
|
143
|
+
placement: message.placement,
|
|
144
|
+
compartmentKind: message.compartmentKind,
|
|
145
|
+
...(message.ownerEmail ? { ownerEmail: message.ownerEmail } : {}),
|
|
146
|
+
...(message.source ? { source: message.source } : {}),
|
|
147
|
+
};
|
|
148
|
+
fs.writeFileSync(cachePath(message.agentId, message.id), `${JSON.stringify(updated)}\n`, "utf-8");
|
|
149
|
+
const docs = loadCache(message.agentId);
|
|
150
|
+
docs.set(updated.messageId, updated);
|
|
151
|
+
}
|
|
152
|
+
function sourceMatches(source, filter) {
|
|
153
|
+
if (!filter)
|
|
154
|
+
return true;
|
|
155
|
+
if (!source)
|
|
156
|
+
return false;
|
|
157
|
+
return source.toLowerCase() === filter.toLowerCase();
|
|
158
|
+
}
|
|
159
|
+
function searchMailSearchCache(filters) {
|
|
160
|
+
const queryTerms = filters.queryTerms ?? [];
|
|
161
|
+
const docs = [...loadCache(filters.agentId).values()]
|
|
162
|
+
.filter((document) => filters.placement ? document.placement === filters.placement : true)
|
|
163
|
+
.filter((document) => filters.compartmentKind ? document.compartmentKind === filters.compartmentKind : true)
|
|
164
|
+
.filter((document) => sourceMatches(document.source, filters.source))
|
|
165
|
+
.filter((document) => queryTerms.length
|
|
166
|
+
? queryTerms.some((term) => document.searchText.includes(term))
|
|
167
|
+
: true);
|
|
168
|
+
let ordered;
|
|
169
|
+
if (queryTerms.length > 0) {
|
|
170
|
+
ordered = docs
|
|
171
|
+
.map((document) => ({ document, relevance: (0, search_relevance_1.scoreMailSearchDocument)(document, queryTerms) }))
|
|
172
|
+
.sort(search_relevance_1.compareByRelevanceThenRecency)
|
|
173
|
+
.map((entry) => entry.document);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
ordered = docs.sort((left, right) => right.receivedAt.localeCompare(left.receivedAt));
|
|
177
|
+
}
|
|
178
|
+
return typeof filters.limit === "number" ? ordered.slice(0, filters.limit) : ordered;
|
|
179
|
+
}
|
|
180
|
+
function resetMailSearchCacheForTests() {
|
|
181
|
+
cacheStates.clear();
|
|
182
|
+
}
|