@ouro.bot/cli 0.1.0-alpha.480 → 0.1.0-alpha.482
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 +16 -0
- package/dist/heart/daemon/cli-exec.js +49 -4
- package/dist/mailroom/blob-store.js +45 -9
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.482",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Tracked mail imports and hosted mail index repairs now queue completion and failure notices into the live MCP session when one exists, so `check_response` learns about background mail work immediately instead of silently showing no pending messages.",
|
|
8
|
+
"When no MCP session is live, background mail-operation notices now fall back cleanly to the freshest non-blocked outward session instead of depending on inner-dialog-only wakeups.",
|
|
9
|
+
"Hosted Blob duplicate checks now treat unreadable already-existing message blobs as degraded duplicates during import reruns, preventing large delegated HEY archive backfills from crashing on a transient duplicate-read timeout while keeping the `ouro.bot` wrapper version-synced for the release."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.481",
|
|
14
|
+
"changes": [
|
|
15
|
+
"Hosted Blob message downloads now retry once on transient timeout or socket-close failures, so rerunning a delegated HEY archive import does not die on the first flaky duplicate-check read.",
|
|
16
|
+
"Azure Blob mail-store coverage now locks the real duplicate-dedupe retry path that failed in production, keeping historical import reruns resilient while preserving bounded timeout behavior for genuinely stalled blobs.",
|
|
17
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper stay version-synced for the hosted mail import retry hardening release."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.480",
|
|
6
22
|
"changes": [
|
|
@@ -84,6 +84,8 @@ const mbox_import_1 = require("../../mailroom/mbox-import");
|
|
|
84
84
|
const reader_1 = require("../../mailroom/reader");
|
|
85
85
|
const pending_1 = require("../../mind/pending");
|
|
86
86
|
const background_operations_1 = require("../background-operations");
|
|
87
|
+
const session_activity_1 = require("../session-activity");
|
|
88
|
+
const target_resolution_1 = require("../target-resolution");
|
|
87
89
|
const cli_parse_1 = require("./cli-parse");
|
|
88
90
|
const cli_parse_2 = require("./cli-parse");
|
|
89
91
|
const cli_help_1 = require("./cli-help");
|
|
@@ -3409,7 +3411,7 @@ function makeBackgroundOperationId(kind) {
|
|
|
3409
3411
|
const prefix = kind === "mail.import-mbox" ? "op_mail_import" : "op_mail_backfill";
|
|
3410
3412
|
return `${prefix}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
|
|
3411
3413
|
}
|
|
3412
|
-
function notifyMailOperation(agentName, record, deps) {
|
|
3414
|
+
async function notifyMailOperation(agentName, record, deps) {
|
|
3413
3415
|
const lines = [
|
|
3414
3416
|
"[Background Operation]",
|
|
3415
3417
|
`${record.title} ${record.status === "failed" ? "failed" : "finished"}.`,
|
|
@@ -3422,19 +3424,62 @@ function notifyMailOperation(agentName, record, deps) {
|
|
|
3422
3424
|
"",
|
|
3423
3425
|
"Use query_active_work for the live background-work view before drilling into mail details.",
|
|
3424
3426
|
];
|
|
3425
|
-
|
|
3427
|
+
const agentRoot = providerCliAgentRoot({ agent: agentName }, deps);
|
|
3428
|
+
const content = lines.join("\n");
|
|
3429
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", "self", "inner", "dialog"), {
|
|
3426
3430
|
from: "mailroom",
|
|
3427
3431
|
friendId: "self",
|
|
3428
3432
|
channel: "inner",
|
|
3429
3433
|
key: "dialog",
|
|
3430
|
-
content
|
|
3434
|
+
content,
|
|
3431
3435
|
timestamp: deps.now?.() ?? Date.now(),
|
|
3432
3436
|
mode: "reflect",
|
|
3433
3437
|
});
|
|
3434
|
-
|
|
3438
|
+
await queueMailOperationTargetNotification(agentName, content, deps).catch(() => undefined);
|
|
3439
|
+
await deps.sendCommand(deps.socketPath, { kind: "inner.wake", agent: agentName })
|
|
3435
3440
|
.then(() => undefined)
|
|
3436
3441
|
.catch(() => undefined);
|
|
3437
3442
|
}
|
|
3443
|
+
async function queueMailOperationTargetNotification(agentName, content, deps) {
|
|
3444
|
+
const agentRoot = providerCliAgentRoot({ agent: agentName }, deps);
|
|
3445
|
+
const friendsDir = path.join(agentRoot, "friends");
|
|
3446
|
+
const sessionsDir = path.join(agentRoot, "state", "sessions");
|
|
3447
|
+
const mcpTarget = (0, session_activity_1.listSessionActivity)({
|
|
3448
|
+
sessionsDir,
|
|
3449
|
+
friendsDir,
|
|
3450
|
+
agentName,
|
|
3451
|
+
}).find((candidate) => candidate.channel === "mcp");
|
|
3452
|
+
if (mcpTarget) {
|
|
3453
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", mcpTarget.friendId, mcpTarget.channel, mcpTarget.key), {
|
|
3454
|
+
from: "mailroom",
|
|
3455
|
+
friendId: mcpTarget.friendId,
|
|
3456
|
+
channel: mcpTarget.channel,
|
|
3457
|
+
key: mcpTarget.key,
|
|
3458
|
+
content,
|
|
3459
|
+
timestamp: deps.now?.() ?? Date.now(),
|
|
3460
|
+
mode: "relay",
|
|
3461
|
+
});
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
const candidates = await (0, target_resolution_1.listTargetSessionCandidates)({
|
|
3465
|
+
sessionsDir,
|
|
3466
|
+
friendsDir,
|
|
3467
|
+
agentName,
|
|
3468
|
+
friendStore: new store_file_1.FileFriendStore(friendsDir),
|
|
3469
|
+
});
|
|
3470
|
+
const target = candidates.find((candidate) => candidate.delivery.mode !== "blocked");
|
|
3471
|
+
if (!target)
|
|
3472
|
+
return;
|
|
3473
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", target.friendId, target.channel, target.key), {
|
|
3474
|
+
from: "mailroom",
|
|
3475
|
+
friendId: target.friendId,
|
|
3476
|
+
channel: target.channel,
|
|
3477
|
+
key: target.key,
|
|
3478
|
+
content,
|
|
3479
|
+
timestamp: deps.now?.() ?? Date.now(),
|
|
3480
|
+
mode: "relay",
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3438
3483
|
function ensureTrackedMailOperation(input) {
|
|
3439
3484
|
if (!input.operationId)
|
|
3440
3485
|
return null;
|
|
@@ -9,6 +9,7 @@ const MESSAGE_INDEX_SORT_MAX_MS = 9_999_999_999_999;
|
|
|
9
9
|
const MESSAGE_INDEX_SORT_WIDTH = 13;
|
|
10
10
|
const MESSAGE_INDEX_NO_SOURCE = "~";
|
|
11
11
|
const DEFAULT_BLOB_OPERATION_TIMEOUT_MS = 20_000;
|
|
12
|
+
const DEFAULT_BLOB_DOWNLOAD_ATTEMPTS = 2;
|
|
12
13
|
const DEFAULT_MESSAGE_FETCH_CONCURRENCY = 20;
|
|
13
14
|
const DEFAULT_MESSAGE_INDEX_BACKFILL_CONCURRENCY = 8;
|
|
14
15
|
const MESSAGE_LIST_SCAN_CONCURRENCY = 32;
|
|
@@ -64,18 +65,32 @@ function normalizeBlobOperationError(action, blob, timeoutMs, error) {
|
|
|
64
65
|
}
|
|
65
66
|
return new Error(`${action} ${blobClientName(blob)} failed: ${message}`);
|
|
66
67
|
}
|
|
68
|
+
function isRetryableBlobDownloadError(error) {
|
|
69
|
+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
70
|
+
return message.includes("timed out") ||
|
|
71
|
+
message.includes("abort") ||
|
|
72
|
+
message.includes("econnreset") ||
|
|
73
|
+
message.includes("etimedout") ||
|
|
74
|
+
message.includes("socket closed");
|
|
75
|
+
}
|
|
67
76
|
async function downloadJson(blob, timeoutMs) {
|
|
68
77
|
if (!await blob.exists())
|
|
69
78
|
return null;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
let lastError = null;
|
|
80
|
+
for (let attempt = 1; attempt <= DEFAULT_BLOB_DOWNLOAD_ATTEMPTS; attempt += 1) {
|
|
81
|
+
try {
|
|
82
|
+
const buffer = await withBlobOperationTimeout(timeoutMs, (abortSignal) => {
|
|
83
|
+
return blob.downloadToBuffer(undefined, undefined, { abortSignal });
|
|
84
|
+
});
|
|
85
|
+
return JSON.parse(buffer.toString("utf-8"));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
lastError = error;
|
|
89
|
+
if (attempt >= DEFAULT_BLOB_DOWNLOAD_ATTEMPTS || !isRetryableBlobDownloadError(error))
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
78
92
|
}
|
|
93
|
+
throw normalizeBlobOperationError("download", blob, timeoutMs, lastError);
|
|
79
94
|
}
|
|
80
95
|
async function uploadJson(blob, value, timeoutMs) {
|
|
81
96
|
try {
|
|
@@ -321,7 +336,28 @@ class AzureBlobMailroomStore {
|
|
|
321
336
|
async putRawMessage(input) {
|
|
322
337
|
await this.ensureContainer();
|
|
323
338
|
const { message, rawPayload, candidate } = await (0, core_1.buildStoredMailMessage)(input);
|
|
324
|
-
const
|
|
339
|
+
const messageBlob = this.messageBlob(message.id);
|
|
340
|
+
let existing = null;
|
|
341
|
+
try {
|
|
342
|
+
existing = await downloadJson(messageBlob, this.blobOperationTimeoutMs);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
if (isRetryableBlobDownloadError(error) && await messageBlob.exists().catch(() => false)) {
|
|
346
|
+
(0, runtime_1.emitNervesEvent)({
|
|
347
|
+
level: "warn",
|
|
348
|
+
component: "senses",
|
|
349
|
+
event: "senses.mail_blob_store_dedupe_degraded",
|
|
350
|
+
message: "azure blob mailroom store treated an unreadable existing message as a duplicate",
|
|
351
|
+
meta: {
|
|
352
|
+
id: message.id,
|
|
353
|
+
agentId: message.agentId,
|
|
354
|
+
error: error instanceof Error ? error.message : String(error),
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
return { created: false, message };
|
|
358
|
+
}
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
325
361
|
if (existing) {
|
|
326
362
|
await this.putMessageIndex(existing);
|
|
327
363
|
(0, runtime_1.emitNervesEvent)({
|