@ouro.bot/cli 0.1.0-alpha.485 → 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 +9 -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/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/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/mail.js +19 -3
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
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.486",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Mail convergence pass 1-5 hardens hosted-mail truth surfaces under live HEY ingest: import truth + audit resilience, accurate archive freshness and identity surfaces, sharper recovery and archive truth, delegated search resilience, and natural anchor-list retrieval; imported archive content is now searched on parsed message text rather than raw archive bytes so quoted-printable / HTML-heavy booking mail is reachable.",
|
|
8
|
+
"`mail_search` now ranks by booking-aware relevance instead of pure recency. Score signals weight query-term hits by field (subject +6 / from +4 / body +2), booking-intent tokens (`booking confirmation`, `your stay`, e-ticket, etc.), confirmation-number-shaped tokens, currency amounts, and known travel-sender domains; recency stays as a tiebreaker. Recall is unchanged — noise still appears in results, just below the decisive message. Each rendered result also surfaces a `matched on:` line listing fields, booking signals, status (confirmed / cancelled / changed / refunded / etc.), confirmation token, amount, dates, attachment count, and sender hint, so the agent can triage without paying for a body open.",
|
|
9
|
+
"BlueBubbles sense no longer sticks in `error` status when a single message is permanently unrecoverable. `upstreamStatus` now tracks upstream health and pending work only — per-cycle recovery failures stay informational in `detail` for transparency without contradicting `ouro doctor`'s healthy verdict, so a malformed payload that fails repairEvent on every retry can no longer brick the visible sense state until operator intervention.",
|
|
10
|
+
"Heart streaming caps oversized Responses-API `function_call_output` history items both when rebuilding provider input from session history and when appending fresh tool output mid-turn, preventing a giant tool result on resume from blowing the model context."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.485",
|
|
6
15
|
"changes": [
|
|
@@ -239,6 +239,10 @@ function backgroundOperationPriority(operation) {
|
|
|
239
239
|
case "succeeded": return 3;
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
+
function backgroundOperationSpecText(spec, key) {
|
|
243
|
+
const value = spec?.[key];
|
|
244
|
+
return typeof value === "string" ? value.trim() : "";
|
|
245
|
+
}
|
|
242
246
|
function selectPrimaryBackgroundOperation(frame) {
|
|
243
247
|
const operations = frame.backgroundOperations ?? [];
|
|
244
248
|
if (operations.length === 0)
|
|
@@ -257,13 +261,34 @@ function formatBackgroundOperationNextAction(operation) {
|
|
|
257
261
|
if (operation.kind === "mail.import-mbox") {
|
|
258
262
|
switch (operation.status) {
|
|
259
263
|
case "failed":
|
|
264
|
+
switch (operation.failure?.class?.trim()) {
|
|
265
|
+
case "transient-storage-read":
|
|
266
|
+
return "retry-safe once the transient storage/network issue clears";
|
|
267
|
+
case "mailroom-auth":
|
|
268
|
+
case "mailroom-config":
|
|
269
|
+
return "repair mail auth/config access, then retry";
|
|
270
|
+
case "source-grant-missing":
|
|
271
|
+
case "source-grant-ambiguous":
|
|
272
|
+
return "repair the delegated owner/source lane, then retry";
|
|
273
|
+
case "archive-discovery":
|
|
274
|
+
case "archive-ambiguity":
|
|
275
|
+
case "archive-missing":
|
|
276
|
+
case "archive-access":
|
|
277
|
+
return "materialize or point at the correct local archive, then retry";
|
|
278
|
+
}
|
|
279
|
+
if (operation.failure?.retryDisposition === "retry-safe") {
|
|
280
|
+
return "retry the failed mail import once the transient issue clears or the dependency answers again";
|
|
281
|
+
}
|
|
282
|
+
if (operation.failure?.retryDisposition === "investigate-first") {
|
|
283
|
+
return "inspect the failed mail import carefully, confirm the root cause, and then decide whether to retry";
|
|
284
|
+
}
|
|
260
285
|
return "inspect the failed mail import, fix the issue, and retry it";
|
|
261
286
|
case "queued":
|
|
262
|
-
return "
|
|
287
|
+
return "queued — no action unless it stalls or i need live status";
|
|
263
288
|
case "running":
|
|
264
|
-
return "
|
|
289
|
+
return "in flight — no action unless it stalls or i need live status";
|
|
265
290
|
case "succeeded":
|
|
266
|
-
return "
|
|
291
|
+
return "caught up — no rerun needed unless a newer archive appears";
|
|
267
292
|
}
|
|
268
293
|
}
|
|
269
294
|
switch (operation.status) {
|
|
@@ -273,6 +298,66 @@ function formatBackgroundOperationNextAction(operation) {
|
|
|
273
298
|
case "succeeded": return `review the completed ${operation.title} operation and continue`;
|
|
274
299
|
}
|
|
275
300
|
}
|
|
301
|
+
function formatMailImportRecoveryUniverse(operation) {
|
|
302
|
+
const failureClass = operation.failure?.class?.trim();
|
|
303
|
+
if (!failureClass)
|
|
304
|
+
return null;
|
|
305
|
+
switch (failureClass) {
|
|
306
|
+
case "transient-storage-read":
|
|
307
|
+
return "transient dependency/read issue — safe to retry once storage/network answers again";
|
|
308
|
+
case "mailroom-auth":
|
|
309
|
+
case "mailroom-config":
|
|
310
|
+
return "mail auth/config issue — repair mail access or runtime config before retrying";
|
|
311
|
+
case "source-grant-missing":
|
|
312
|
+
case "source-grant-ambiguous":
|
|
313
|
+
return "delegated lane/registry issue — inspect owner/source linking before retrying";
|
|
314
|
+
case "archive-discovery":
|
|
315
|
+
case "archive-ambiguity":
|
|
316
|
+
case "archive-missing":
|
|
317
|
+
case "archive-access":
|
|
318
|
+
return "local archive/file issue — materialize or point at the right archive before retrying";
|
|
319
|
+
default:
|
|
320
|
+
return "unclassified import issue — inspect the recorded error before retrying";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function formatBackgroundOperationMeta(operation) {
|
|
324
|
+
const lines = [`operation: ${operation.id}`];
|
|
325
|
+
const filePath = backgroundOperationSpecText(operation.spec, "filePath")
|
|
326
|
+
|| backgroundOperationSpecText(operation.spec, "newestCandidatePath");
|
|
327
|
+
if (filePath)
|
|
328
|
+
lines.push(`file: ${filePath}`);
|
|
329
|
+
const originLabel = backgroundOperationSpecText(operation.spec, "fileOriginLabel")
|
|
330
|
+
|| backgroundOperationSpecText(operation.spec, "newestCandidateOriginLabel");
|
|
331
|
+
if (originLabel)
|
|
332
|
+
lines.push(`origin: ${originLabel}`);
|
|
333
|
+
const ownerEmail = backgroundOperationSpecText(operation.spec, "ownerEmail");
|
|
334
|
+
const source = backgroundOperationSpecText(operation.spec, "source");
|
|
335
|
+
if (ownerEmail || source) {
|
|
336
|
+
lines.push(`owner/source: ${ownerEmail || "unknown"} / ${source || "unknown"}`);
|
|
337
|
+
}
|
|
338
|
+
if (operation.failure?.class?.trim())
|
|
339
|
+
lines.push(`failure class: ${operation.failure.class}`);
|
|
340
|
+
if (operation.failure?.retryDisposition?.trim())
|
|
341
|
+
lines.push(`retry: ${operation.failure.retryDisposition}`);
|
|
342
|
+
if (operation.failure?.hint?.trim())
|
|
343
|
+
lines.push(`recovery: ${operation.failure.hint}`);
|
|
344
|
+
const recoveryUniverse = operation.kind === "mail.import-mbox" ? formatMailImportRecoveryUniverse(operation) : null;
|
|
345
|
+
if (recoveryUniverse)
|
|
346
|
+
lines.push(`recovery universe: ${recoveryUniverse}`);
|
|
347
|
+
if (operation.startedAt?.trim())
|
|
348
|
+
lines.push(`started: ${operation.startedAt}`);
|
|
349
|
+
if (operation.finishedAt?.trim())
|
|
350
|
+
lines.push(`finished: ${operation.finishedAt}`);
|
|
351
|
+
else if (operation.updatedAt?.trim())
|
|
352
|
+
lines.push(`updated: ${operation.updatedAt}`);
|
|
353
|
+
const nextAction = formatBackgroundOperationNextAction(operation);
|
|
354
|
+
if (nextAction.trim().length > 0)
|
|
355
|
+
lines.push(`next: ${nextAction}`);
|
|
356
|
+
if (operation.remediation?.length) {
|
|
357
|
+
lines.push(...operation.remediation.map((step) => `remediation: ${step}`));
|
|
358
|
+
}
|
|
359
|
+
return lines;
|
|
360
|
+
}
|
|
276
361
|
function formatNextAction(frame, obligation) {
|
|
277
362
|
const obligationHasConcreteArtifact = Boolean(obligation?.currentArtifact?.trim())
|
|
278
363
|
|| obligation?.currentSurface?.kind === "merge";
|
|
@@ -696,6 +781,7 @@ function formatActiveWorkFrame(frame, options) {
|
|
|
696
781
|
if (operation.summary.trim().length > 0) {
|
|
697
782
|
line += `: ${operation.summary}`;
|
|
698
783
|
}
|
|
784
|
+
line += `\n ${formatBackgroundOperationMeta(operation).join("\n ")}`;
|
|
699
785
|
if (operation.detail?.trim()) {
|
|
700
786
|
line += `\n ${operation.detail.trim()}`;
|
|
701
787
|
}
|
|
@@ -112,6 +112,20 @@ function normalizeRecord(record) {
|
|
|
112
112
|
if (record.error && typeof record.error.message === "string" && record.error.message.trim().length > 0) {
|
|
113
113
|
normalized.error = { message: record.error.message.trim() };
|
|
114
114
|
}
|
|
115
|
+
if (record.failure && typeof record.failure === "object" && !Array.isArray(record.failure)) {
|
|
116
|
+
const failureClass = typeof record.failure.class === "string" ? record.failure.class.trim() : "";
|
|
117
|
+
const retryDisposition = record.failure.retryDisposition;
|
|
118
|
+
const hint = typeof record.failure.hint === "string" ? record.failure.hint.trim() : "";
|
|
119
|
+
if (failureClass) {
|
|
120
|
+
normalized.failure = {
|
|
121
|
+
class: failureClass,
|
|
122
|
+
...(retryDisposition === "retry-safe" || retryDisposition === "fix-before-retry" || retryDisposition === "investigate-first"
|
|
123
|
+
? { retryDisposition }
|
|
124
|
+
: {}),
|
|
125
|
+
...(hint ? { hint } : {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
115
129
|
if (Array.isArray(record.remediation)) {
|
|
116
130
|
const remediation = record.remediation.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
117
131
|
if (remediation.length > 0)
|
|
@@ -143,15 +157,19 @@ function visibleOperationKey(record) {
|
|
|
143
157
|
return `${record.agentName.toLowerCase()}|${record.kind}|${filePath}|${ownerEmail}|${source}`;
|
|
144
158
|
}
|
|
145
159
|
function writeRecord(locator, record) {
|
|
160
|
+
const normalized = normalizeRecord(record);
|
|
161
|
+
if (!normalized) {
|
|
162
|
+
throw new Error(`invalid background operation record: ${locator.id}`);
|
|
163
|
+
}
|
|
146
164
|
fs.mkdirSync(operationsDir(locator.agentName, locator.agentRoot), { recursive: true });
|
|
147
|
-
fs.writeFileSync(operationPath(locator), `${JSON.stringify(
|
|
165
|
+
fs.writeFileSync(operationPath(locator), `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
148
166
|
(0, runtime_1.emitNervesEvent)({
|
|
149
167
|
component: "engine",
|
|
150
168
|
event: "engine.background_operation_written",
|
|
151
169
|
message: "background operation state written",
|
|
152
|
-
meta: { agentName: locator.agentName, id: locator.id, kind:
|
|
170
|
+
meta: { agentName: locator.agentName, id: locator.id, kind: normalized.kind, status: normalized.status },
|
|
153
171
|
});
|
|
154
|
-
return
|
|
172
|
+
return normalized;
|
|
155
173
|
}
|
|
156
174
|
function requireRecord(locator) {
|
|
157
175
|
const record = readBackgroundOperation(locator);
|
|
@@ -226,6 +244,7 @@ function updateBackgroundOperation(input) {
|
|
|
226
244
|
summary: input.summary ?? current.summary,
|
|
227
245
|
...(input.detail ? { detail: input.detail } : {}),
|
|
228
246
|
...(normalizeProgress(input.progress) ? { progress: normalizeProgress(input.progress) } : {}),
|
|
247
|
+
...(input.spec ? { spec: input.spec } : {}),
|
|
229
248
|
updatedAt: input.updatedAt ?? current.updatedAt,
|
|
230
249
|
});
|
|
231
250
|
}
|
|
@@ -238,6 +257,9 @@ function completeBackgroundOperation(input) {
|
|
|
238
257
|
...(input.detail ? { detail: input.detail } : {}),
|
|
239
258
|
...(normalizeProgress(input.progress) ? { progress: normalizeProgress(input.progress) } : {}),
|
|
240
259
|
...(input.result ? { result: input.result } : {}),
|
|
260
|
+
error: undefined,
|
|
261
|
+
remediation: undefined,
|
|
262
|
+
failure: undefined,
|
|
241
263
|
finishedAt: input.finishedAt,
|
|
242
264
|
updatedAt: input.updatedAt ?? input.finishedAt,
|
|
243
265
|
});
|
|
@@ -251,6 +273,7 @@ function failBackgroundOperation(input) {
|
|
|
251
273
|
...(input.detail ? { detail: input.detail } : {}),
|
|
252
274
|
...(normalizeProgress(input.progress) ? { progress: normalizeProgress(input.progress) } : {}),
|
|
253
275
|
error: { message: input.error },
|
|
276
|
+
...(input.failure ? { failure: input.failure } : {}),
|
|
254
277
|
...(input.remediation && input.remediation.length > 0 ? { remediation: input.remediation } : {}),
|
|
255
278
|
finishedAt: input.finishedAt,
|
|
256
279
|
updatedAt: input.updatedAt ?? input.finishedAt,
|
|
@@ -3421,6 +3421,9 @@ async function notifyMailOperation(agentName, record, deps) {
|
|
|
3421
3421
|
`status: ${record.status}`,
|
|
3422
3422
|
`summary: ${record.summary}`,
|
|
3423
3423
|
`detail: ${record.detail}`,
|
|
3424
|
+
...(record.failure?.class ? [`failure class: ${record.failure.class}`] : []),
|
|
3425
|
+
...(record.failure?.retryDisposition ? [`retry: ${record.failure.retryDisposition}`] : []),
|
|
3426
|
+
...(record.failure?.hint ? [`recovery: ${record.failure.hint}`] : []),
|
|
3424
3427
|
...(record.error?.message ? [`error: ${record.error.message}`] : []),
|
|
3425
3428
|
...(record.remediation && record.remediation.length > 0 ? ["", "next steps:", ...record.remediation.map((step) => `- ${step}`)] : []),
|
|
3426
3429
|
...(record.kind === "mail.import-mbox" && record.status === "succeeded"
|
|
@@ -3518,7 +3521,7 @@ function ensureTrackedMailOperation(input) {
|
|
|
3518
3521
|
...(progress ? { progress } : {}),
|
|
3519
3522
|
});
|
|
3520
3523
|
},
|
|
3521
|
-
update: (summary, detail, progress) => {
|
|
3524
|
+
update: (summary, detail, progress, spec) => {
|
|
3522
3525
|
(0, background_operations_1.updateBackgroundOperation)({
|
|
3523
3526
|
agentName: input.agentName,
|
|
3524
3527
|
agentRoot,
|
|
@@ -3527,6 +3530,7 @@ function ensureTrackedMailOperation(input) {
|
|
|
3527
3530
|
updatedAt: cliNowIso(input.deps),
|
|
3528
3531
|
...(detail ? { detail } : {}),
|
|
3529
3532
|
...(progress ? { progress } : {}),
|
|
3533
|
+
...(spec ? { spec } : {}),
|
|
3530
3534
|
});
|
|
3531
3535
|
},
|
|
3532
3536
|
succeed: async (summary, detail, result) => {
|
|
@@ -3541,7 +3545,7 @@ function ensureTrackedMailOperation(input) {
|
|
|
3541
3545
|
});
|
|
3542
3546
|
await notifyMailOperation(input.agentName, { ...completed, detail }, input.deps);
|
|
3543
3547
|
},
|
|
3544
|
-
fail: async (error, summary, detail, remediation) => {
|
|
3548
|
+
fail: async (error, summary, detail, remediation, failure) => {
|
|
3545
3549
|
const failed = (0, background_operations_1.failBackgroundOperation)({
|
|
3546
3550
|
agentName: input.agentName,
|
|
3547
3551
|
agentRoot,
|
|
@@ -3550,6 +3554,7 @@ function ensureTrackedMailOperation(input) {
|
|
|
3550
3554
|
summary,
|
|
3551
3555
|
detail,
|
|
3552
3556
|
error: error instanceof Error ? error.message : String(error),
|
|
3557
|
+
...(failure ? { failure } : {}),
|
|
3553
3558
|
remediation,
|
|
3554
3559
|
});
|
|
3555
3560
|
await notifyMailOperation(input.agentName, { ...failed, detail }, input.deps);
|
|
@@ -3608,6 +3613,143 @@ function buildMailImportOperationSpec(filePath, ownerEmail, source) {
|
|
|
3608
3613
|
fileModifiedAt: new Date(stats.mtimeMs).toISOString(),
|
|
3609
3614
|
};
|
|
3610
3615
|
}
|
|
3616
|
+
function classifyMailImportFailure(error) {
|
|
3617
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3618
|
+
const normalized = message.toLowerCase();
|
|
3619
|
+
if (normalized.includes("auth_required:mailroom") || normalized.includes("cannot read mailroom config")) {
|
|
3620
|
+
return {
|
|
3621
|
+
failure: {
|
|
3622
|
+
class: "mailroom-auth",
|
|
3623
|
+
retryDisposition: "fix-before-retry",
|
|
3624
|
+
hint: "mailroom credentials or vault access are unavailable; unlock or repair mail auth first",
|
|
3625
|
+
},
|
|
3626
|
+
remediation: [
|
|
3627
|
+
"unlock or repair the owning agent vault/runtime config, then rerun the import",
|
|
3628
|
+
"use query_active_work to confirm the failed operation has settled before retrying",
|
|
3629
|
+
],
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
if (normalized.includes("missing mailroom config")
|
|
3633
|
+
|| normalized.includes("missing registrypath/storepath")
|
|
3634
|
+
|| normalized.includes("missing hosted registry coordinates")) {
|
|
3635
|
+
return {
|
|
3636
|
+
failure: {
|
|
3637
|
+
class: "mailroom-config",
|
|
3638
|
+
retryDisposition: "fix-before-retry",
|
|
3639
|
+
hint: "mailroom runtime config is incomplete for this agent",
|
|
3640
|
+
},
|
|
3641
|
+
remediation: [
|
|
3642
|
+
`rerun 'ouro connect mail --agent <agent>' or repair the stored mailroom runtime config`,
|
|
3643
|
+
"retry the import only after the config repair is complete",
|
|
3644
|
+
],
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
if (normalized.includes("no enabled mailroom source grant found")) {
|
|
3648
|
+
return {
|
|
3649
|
+
failure: {
|
|
3650
|
+
class: "source-grant-missing",
|
|
3651
|
+
retryDisposition: "fix-before-retry",
|
|
3652
|
+
hint: "the requested owner/source lane is not provisioned yet",
|
|
3653
|
+
},
|
|
3654
|
+
remediation: [
|
|
3655
|
+
"create or repair the delegated source grant for the intended owner/source, then rerun the import",
|
|
3656
|
+
"confirm the import is pointed at the intended owner/source before retrying",
|
|
3657
|
+
],
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3660
|
+
if (normalized.includes("multiple source grants found")) {
|
|
3661
|
+
return {
|
|
3662
|
+
failure: {
|
|
3663
|
+
class: "source-grant-ambiguous",
|
|
3664
|
+
retryDisposition: "fix-before-retry",
|
|
3665
|
+
hint: "more than one source grant matches; the import needs a narrower owner/source target",
|
|
3666
|
+
},
|
|
3667
|
+
remediation: [
|
|
3668
|
+
"rerun the import with explicit --owner-email and/or --source hints",
|
|
3669
|
+
"confirm which delegated lane should receive this archive before retrying",
|
|
3670
|
+
],
|
|
3671
|
+
};
|
|
3672
|
+
}
|
|
3673
|
+
if (normalized.includes("could not discover an mbox file")) {
|
|
3674
|
+
return {
|
|
3675
|
+
failure: {
|
|
3676
|
+
class: "archive-discovery",
|
|
3677
|
+
retryDisposition: "fix-before-retry",
|
|
3678
|
+
hint: "no matching local archive was discovered in the known browser/download locations",
|
|
3679
|
+
},
|
|
3680
|
+
remediation: [
|
|
3681
|
+
"check the recent browser/download artifact locations or pass --file with the exact archive path",
|
|
3682
|
+
"retry only after the correct archive is visible locally",
|
|
3683
|
+
],
|
|
3684
|
+
};
|
|
3685
|
+
}
|
|
3686
|
+
if (normalized.includes("multiple candidate mbox files found")) {
|
|
3687
|
+
return {
|
|
3688
|
+
failure: {
|
|
3689
|
+
class: "archive-ambiguity",
|
|
3690
|
+
retryDisposition: "fix-before-retry",
|
|
3691
|
+
hint: "more than one plausible archive matches this import request",
|
|
3692
|
+
},
|
|
3693
|
+
remediation: [
|
|
3694
|
+
"rerun with --file <path> or narrower owner/source hints so the archive choice is unambiguous",
|
|
3695
|
+
"retry after confirming which archive is the intended one",
|
|
3696
|
+
],
|
|
3697
|
+
};
|
|
3698
|
+
}
|
|
3699
|
+
if (normalized.includes("no such file:") || normalized.includes("enoent")) {
|
|
3700
|
+
return {
|
|
3701
|
+
failure: {
|
|
3702
|
+
class: "archive-missing",
|
|
3703
|
+
retryDisposition: "fix-before-retry",
|
|
3704
|
+
hint: "the chosen archive path is not readable on disk anymore",
|
|
3705
|
+
},
|
|
3706
|
+
remediation: [
|
|
3707
|
+
"re-check the local archive path or re-download the export before retrying",
|
|
3708
|
+
"retry after the archive exists locally again",
|
|
3709
|
+
],
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
if (normalized.includes("permission denied") || normalized.includes("eacces") || normalized.includes("eperm")) {
|
|
3713
|
+
return {
|
|
3714
|
+
failure: {
|
|
3715
|
+
class: "archive-access",
|
|
3716
|
+
retryDisposition: "fix-before-retry",
|
|
3717
|
+
hint: "the archive or backing store could not be read with current filesystem permissions",
|
|
3718
|
+
},
|
|
3719
|
+
remediation: [
|
|
3720
|
+
"repair filesystem access to the archive or backing store, then rerun the import",
|
|
3721
|
+
"retry after confirming the path is readable by Ouro",
|
|
3722
|
+
],
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
if (normalized.includes("timed out")
|
|
3726
|
+
|| normalized.includes("socket closed early")
|
|
3727
|
+
|| normalized.includes("econnreset")
|
|
3728
|
+
|| normalized.includes("eai_again")) {
|
|
3729
|
+
return {
|
|
3730
|
+
failure: {
|
|
3731
|
+
class: "transient-storage-read",
|
|
3732
|
+
retryDisposition: "retry-safe",
|
|
3733
|
+
hint: "likely transient hosted read failure",
|
|
3734
|
+
},
|
|
3735
|
+
remediation: [
|
|
3736
|
+
"retry the import from the same archive after the transient storage/network issue clears",
|
|
3737
|
+
"use query_active_work to confirm the failed operation has settled before retrying",
|
|
3738
|
+
],
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
return {
|
|
3742
|
+
failure: {
|
|
3743
|
+
class: "unknown-mail-import-failure",
|
|
3744
|
+
retryDisposition: "investigate-first",
|
|
3745
|
+
hint: "the import failed for a reason that does not match a known recovery class yet",
|
|
3746
|
+
},
|
|
3747
|
+
remediation: [
|
|
3748
|
+
"inspect the recorded error and surrounding Mailroom/runtime state before retrying",
|
|
3749
|
+
"retry only after the likely cause is understood or ruled out",
|
|
3750
|
+
],
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3611
3753
|
function matchingMailImportOperation(record, command, filePath) {
|
|
3612
3754
|
if (record.kind !== "mail.import-mbox")
|
|
3613
3755
|
return false;
|
|
@@ -3683,8 +3825,8 @@ function resolveMailImportFilePath(command, deps) {
|
|
|
3683
3825
|
});
|
|
3684
3826
|
}
|
|
3685
3827
|
async function executeMailImportMbox(command, deps) {
|
|
3686
|
-
const filePath = resolveMailImportFilePath(command, deps);
|
|
3687
3828
|
if (!command.foreground) {
|
|
3829
|
+
const filePath = resolveMailImportFilePath(command, deps);
|
|
3688
3830
|
if (!fs.existsSync(filePath)) {
|
|
3689
3831
|
throw new Error(`no such file: ${filePath}`);
|
|
3690
3832
|
}
|
|
@@ -3707,7 +3849,7 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3707
3849
|
], "mail import", "queued delegated mail import", spec);
|
|
3708
3850
|
}
|
|
3709
3851
|
const progress = createHumanCommandProgress(deps, "mail import");
|
|
3710
|
-
|
|
3852
|
+
let filePath = command.filePath ? path.resolve(command.filePath) : null;
|
|
3711
3853
|
const trackedOperation = ensureTrackedMailOperation({
|
|
3712
3854
|
agentName: command.agent,
|
|
3713
3855
|
deps,
|
|
@@ -3715,13 +3857,28 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3715
3857
|
kind: "mail.import-mbox",
|
|
3716
3858
|
title: "mail import",
|
|
3717
3859
|
queuedSummary: "queued delegated mail import",
|
|
3718
|
-
spec
|
|
3860
|
+
spec: {
|
|
3861
|
+
...(filePath ? { filePath } : {}),
|
|
3862
|
+
...(command.ownerEmail ? { ownerEmail: command.ownerEmail } : {}),
|
|
3863
|
+
...(command.source ? { source: command.source } : {}),
|
|
3864
|
+
...(command.discover ? { discovery: true } : {}),
|
|
3865
|
+
},
|
|
3719
3866
|
});
|
|
3720
3867
|
try {
|
|
3868
|
+
trackedOperation?.running("resolving mail archive", filePath ? `file: ${filePath}` : "mode: discover", {
|
|
3869
|
+
current: 0,
|
|
3870
|
+
unit: "messages",
|
|
3871
|
+
});
|
|
3872
|
+
filePath = resolveMailImportFilePath(command, deps);
|
|
3873
|
+
const spec = buildMailImportOperationSpec(filePath, command.ownerEmail, command.source);
|
|
3721
3874
|
trackedOperation?.running("reading Mailroom config", `file: ${filePath}`, {
|
|
3722
3875
|
current: 0,
|
|
3723
3876
|
unit: "messages",
|
|
3724
3877
|
});
|
|
3878
|
+
trackedOperation?.update("reading Mailroom config", `file: ${filePath}`, {
|
|
3879
|
+
current: 0,
|
|
3880
|
+
unit: "messages",
|
|
3881
|
+
}, spec);
|
|
3725
3882
|
progress.startPhase("reading Mailroom config");
|
|
3726
3883
|
const runtime = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true });
|
|
3727
3884
|
if (!runtime.ok) {
|
|
@@ -3799,10 +3956,15 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3799
3956
|
}
|
|
3800
3957
|
catch (error) {
|
|
3801
3958
|
progress.end();
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3959
|
+
const classifiedFailure = classifyMailImportFailure(error);
|
|
3960
|
+
const failureDetail = filePath
|
|
3961
|
+
? `file: ${filePath}`
|
|
3962
|
+
: [
|
|
3963
|
+
"mode: discover",
|
|
3964
|
+
...(command.ownerEmail ? [`owner: ${command.ownerEmail}`] : []),
|
|
3965
|
+
...(command.source ? [`source: ${command.source}`] : []),
|
|
3966
|
+
].join("; ");
|
|
3967
|
+
await trackedOperation?.fail(error, "delegated mail import failed", failureDetail, classifiedFailure.remediation, classifiedFailure.failure);
|
|
3806
3968
|
throw error;
|
|
3807
3969
|
}
|
|
3808
3970
|
}
|
|
@@ -111,6 +111,26 @@ function listChildDirs(dir) {
|
|
|
111
111
|
return [];
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
function classifyDiscoveredMboxOrigin(dir) {
|
|
115
|
+
const normalizedDir = path.resolve(dir);
|
|
116
|
+
const parts = normalizedDir.split(path.sep).filter(Boolean);
|
|
117
|
+
if (parts.includes(".playwright-mcp")) {
|
|
118
|
+
return {
|
|
119
|
+
originKind: "playwright-sandbox",
|
|
120
|
+
originLabel: "browser sandbox (.playwright-mcp)",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (path.basename(normalizedDir) === "Downloads") {
|
|
124
|
+
return {
|
|
125
|
+
originKind: "downloads",
|
|
126
|
+
originLabel: "Downloads",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
originKind: "filesystem",
|
|
131
|
+
originLabel: "filesystem",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
114
134
|
function findWorktreePools(rootDir, maxDepth) {
|
|
115
135
|
const seen = new Set();
|
|
116
136
|
const found = [];
|
|
@@ -176,12 +196,19 @@ function listDiscoveredMboxCandidates(dir) {
|
|
|
176
196
|
if (!fs.existsSync(dir))
|
|
177
197
|
return [];
|
|
178
198
|
try {
|
|
199
|
+
const origin = classifyDiscoveredMboxOrigin(dir);
|
|
179
200
|
return fs.readdirSync(dir, { withFileTypes: true })
|
|
180
201
|
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".mbox"))
|
|
181
202
|
.map((entry) => {
|
|
182
203
|
const candidatePath = path.join(dir, entry.name);
|
|
183
204
|
const stat = fs.statSync(candidatePath);
|
|
184
|
-
return {
|
|
205
|
+
return {
|
|
206
|
+
path: candidatePath,
|
|
207
|
+
name: entry.name,
|
|
208
|
+
mtimeMs: stat.mtimeMs,
|
|
209
|
+
originKind: origin.originKind,
|
|
210
|
+
originLabel: origin.originLabel,
|
|
211
|
+
};
|
|
185
212
|
});
|
|
186
213
|
}
|
|
187
214
|
catch {
|
|
@@ -244,7 +271,7 @@ function summarizeAmbientImportCandidates(candidates, candidateLimit) {
|
|
|
244
271
|
: `${visibleCandidates.length} recent MBOX archives ready for import`;
|
|
245
272
|
const detailLines = [
|
|
246
273
|
"recent candidates:",
|
|
247
|
-
...visibleCandidates.map((candidate) => `- ${candidate.path}`),
|
|
274
|
+
...visibleCandidates.map((candidate) => `- [${candidate.originLabel}] ${candidate.path}`),
|
|
248
275
|
...(hiddenCount > 0 ? [`- ...and ${hiddenCount} more recent archive${hiddenCount === 1 ? "" : "s"}`] : []),
|
|
249
276
|
"next: if one matches an outstanding mail backfill, run `ouro mail import-mbox --discover` with owner/source hints so Ouro can select the right archive or report ambiguity.",
|
|
250
277
|
];
|
|
@@ -254,8 +281,16 @@ function summarizeAmbientImportCandidates(candidates, candidateLimit) {
|
|
|
254
281
|
spec: {
|
|
255
282
|
fingerprint: recentImportFingerprint(candidates),
|
|
256
283
|
candidatePaths: visibleCandidates.map((candidate) => candidate.path),
|
|
284
|
+
candidateDescriptors: visibleCandidates.map((candidate) => ({
|
|
285
|
+
path: candidate.path,
|
|
286
|
+
originKind: candidate.originKind,
|
|
287
|
+
originLabel: candidate.originLabel,
|
|
288
|
+
modifiedAt: new Date(candidate.mtimeMs).toISOString(),
|
|
289
|
+
})),
|
|
257
290
|
newestCandidatePath: visibleCandidates[0]?.path ?? null,
|
|
258
291
|
newestCandidateMtime: visibleCandidates[0] ? new Date(visibleCandidates[0].mtimeMs).toISOString() : null,
|
|
292
|
+
newestCandidateOriginKind: visibleCandidates[0]?.originKind ?? null,
|
|
293
|
+
newestCandidateOriginLabel: visibleCandidates[0]?.originLabel ?? null,
|
|
259
294
|
},
|
|
260
295
|
};
|
|
261
296
|
}
|
|
@@ -120,7 +120,7 @@ function createAzureProviderRuntime(model, azureConfig = (0, config_1.getAzureCo
|
|
|
120
120
|
appendToolOutput(callId, output) {
|
|
121
121
|
if (!nativeInput)
|
|
122
122
|
return;
|
|
123
|
-
nativeInput.push({ type: "function_call_output", call_id: callId, output });
|
|
123
|
+
nativeInput.push({ type: "function_call_output", call_id: callId, output: (0, streaming_1.truncateResponsesFunctionCallOutput)(output) });
|
|
124
124
|
},
|
|
125
125
|
async streamTurn(request) {
|
|
126
126
|
if (!nativeInput)
|
|
@@ -102,7 +102,7 @@ function createGithubCopilotProviderRuntime(model, config = (0, config_1.getGith
|
|
|
102
102
|
appendToolOutput(callId, output) {
|
|
103
103
|
if (!nativeInput)
|
|
104
104
|
return;
|
|
105
|
-
nativeInput.push({ type: "function_call_output", call_id: callId, output });
|
|
105
|
+
nativeInput.push({ type: "function_call_output", call_id: callId, output: (0, streaming_1.truncateResponsesFunctionCallOutput)(output) });
|
|
106
106
|
},
|
|
107
107
|
async streamTurn(request) {
|
|
108
108
|
if (!nativeInput)
|
|
@@ -154,7 +154,7 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
|
|
|
154
154
|
appendToolOutput(callId, output) {
|
|
155
155
|
if (!nativeInput)
|
|
156
156
|
return;
|
|
157
|
-
nativeInput.push({ type: "function_call_output", call_id: callId, output });
|
|
157
|
+
nativeInput.push({ type: "function_call_output", call_id: callId, output: (0, streaming_1.truncateResponsesFunctionCallOutput)(output) });
|
|
158
158
|
},
|
|
159
159
|
async streamTurn(request) {
|
|
160
160
|
if (!nativeInput)
|
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
|
}
|