@ouro.bot/cli 0.1.0-alpha.481 → 0.1.0-alpha.483
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/active-work.js +46 -0
- package/dist/heart/background-operations.js +27 -3
- package/dist/heart/core.js +15 -0
- package/dist/heart/daemon/cli-exec.js +153 -19
- package/dist/heart/daemon/cli-help.js +10 -4
- package/dist/heart/daemon/cli-parse.js +11 -5
- package/dist/heart/daemon/cli-render.js +1 -1
- package/dist/heart/daemon/thoughts.js +22 -8
- package/dist/heart/mail-import-discovery.js +318 -0
- package/dist/heart/outlook/outlook-http-routes.js +1 -1
- package/dist/heart/outlook/outlook-http-static.js +4 -0
- package/dist/heart/outlook/outlook-http.js +2 -2
- package/dist/heart/outlook/outlook-types.js +1 -1
- package/dist/heart/outlook/outlook-view.js +3 -3
- package/dist/heart/outlook/readers/agent-machine.js +34 -11
- package/dist/heart/outlook/readers/continuity-readers.js +5 -1
- package/dist/heart/outlook/readers/mail.js +3 -3
- package/dist/heart/session-events.js +91 -5
- package/dist/heart/turn-context.js +11 -0
- package/dist/mailroom/blob-store.js +22 -1
- package/dist/mind/context.js +1 -1
- package/dist/mind/prompt.js +6 -3
- package/dist/nerves/coverage/file-completeness.js +1 -0
- package/dist/nerves/observation.js +2 -2
- package/dist/outlook-ui/assets/{index-CPfhbn13.js → index-Cm51CY9W.js} +1 -1
- package/dist/outlook-ui/index.html +2 -2
- package/dist/repertoire/tools-mail.js +2 -2
- package/dist/repertoire/tools-session.js +5 -2
- package/dist/senses/cli/ouro-tui.js +1 -1
- package/dist/senses/inner-dialog.js +5 -7
- package/dist/senses/mail.js +149 -18
- package/dist/senses/pipeline.js +2 -1
- 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.483",
|
|
6
|
+
"changes": [
|
|
7
|
+
"`ouro mail import-mbox --discover` can now find HEY exports downloaded through the browser MCP sandbox as well as `~/Downloads`, so delegated backfill no longer depends on a human hand-copying Playwright-normalized `.mbox` paths.",
|
|
8
|
+
"Inner-dialog truth surfaces now strip synthetic timestamp noise and understand live v2 session envelopes, which makes `ouro inner`, `ouro thoughts`, and shared-work status checks report the real recent state instead of stale or empty transcript tails.",
|
|
9
|
+
"Ouro Mailbox is now the human-facing name for the read-only mailbox surface, and stale dead coding lanes no longer masquerade as live return-ready work inside Mailbox obligation and needs-me views while the `ouro.bot` wrapper stays version-synced for the release."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.482",
|
|
14
|
+
"changes": [
|
|
15
|
+
"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.",
|
|
16
|
+
"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.",
|
|
17
|
+
"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."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.481",
|
|
6
22
|
"changes": [
|
|
@@ -231,6 +231,48 @@ function formatObligationContentNextAction(obligation) {
|
|
|
231
231
|
return null;
|
|
232
232
|
return `work on "${content}" and bring back a concrete artifact`;
|
|
233
233
|
}
|
|
234
|
+
function backgroundOperationPriority(operation) {
|
|
235
|
+
switch (operation.status) {
|
|
236
|
+
case "failed": return 0;
|
|
237
|
+
case "queued": return 1;
|
|
238
|
+
case "running": return 2;
|
|
239
|
+
case "succeeded": return 3;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function selectPrimaryBackgroundOperation(frame) {
|
|
243
|
+
const operations = frame.backgroundOperations ?? [];
|
|
244
|
+
if (operations.length === 0)
|
|
245
|
+
return null;
|
|
246
|
+
return [...operations].sort((left, right) => {
|
|
247
|
+
const priorityDiff = backgroundOperationPriority(left) - backgroundOperationPriority(right);
|
|
248
|
+
if (priorityDiff !== 0)
|
|
249
|
+
return priorityDiff;
|
|
250
|
+
return Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
|
|
251
|
+
})[0] ?? null;
|
|
252
|
+
}
|
|
253
|
+
function formatBackgroundOperationNextAction(operation) {
|
|
254
|
+
if (operation.kind === "mail.import-discovered") {
|
|
255
|
+
return "inspect the import-ready archive and start the matching mail import";
|
|
256
|
+
}
|
|
257
|
+
if (operation.kind === "mail.import-mbox") {
|
|
258
|
+
switch (operation.status) {
|
|
259
|
+
case "failed":
|
|
260
|
+
return "inspect the failed mail import, fix the issue, and retry it";
|
|
261
|
+
case "queued":
|
|
262
|
+
return "let the queued mail import start; failure or completion will wake me, so i only re-check if i need status or it looks stalled";
|
|
263
|
+
case "running":
|
|
264
|
+
return "let the background mail import run; failure or completion will wake me, so i only check again if i need status or it looks stalled";
|
|
265
|
+
case "succeeded":
|
|
266
|
+
return "review the completed mail import and continue from the updated mailbox state; i only rerun if a newer archive appears";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
switch (operation.status) {
|
|
270
|
+
case "failed": return `repair the failed ${operation.title} operation and retry it`;
|
|
271
|
+
case "queued": return `let the queued ${operation.title} operation start, then re-check progress`;
|
|
272
|
+
case "running": return `monitor the ${operation.title} operation and react when it changes`;
|
|
273
|
+
case "succeeded": return `review the completed ${operation.title} operation and continue`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
234
276
|
function formatNextAction(frame, obligation) {
|
|
235
277
|
const obligationHasConcreteArtifact = Boolean(obligation?.currentArtifact?.trim())
|
|
236
278
|
|| obligation?.currentSurface?.kind === "merge";
|
|
@@ -258,6 +300,10 @@ function formatNextAction(frame, obligation) {
|
|
|
258
300
|
if (obligation) {
|
|
259
301
|
return formatObligationContentNextAction(obligation) || "continue the active loop and bring the result back here";
|
|
260
302
|
}
|
|
303
|
+
const backgroundOperation = selectPrimaryBackgroundOperation(frame);
|
|
304
|
+
if (backgroundOperation) {
|
|
305
|
+
return formatBackgroundOperationNextAction(backgroundOperation);
|
|
306
|
+
}
|
|
261
307
|
if (frame.mustResolveBeforeHandoff) {
|
|
262
308
|
return "finish what i started here before moving on";
|
|
263
309
|
}
|
|
@@ -128,6 +128,20 @@ function readRecord(filePath) {
|
|
|
128
128
|
return null;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
+
function specText(spec, key) {
|
|
132
|
+
const value = spec?.[key];
|
|
133
|
+
return typeof value === "string" ? value.trim() : "";
|
|
134
|
+
}
|
|
135
|
+
function visibleOperationKey(record) {
|
|
136
|
+
if (record.kind !== "mail.import-mbox")
|
|
137
|
+
return null;
|
|
138
|
+
const filePath = specText(record.spec, "filePath");
|
|
139
|
+
if (!filePath)
|
|
140
|
+
return null;
|
|
141
|
+
const ownerEmail = specText(record.spec, "ownerEmail").toLowerCase();
|
|
142
|
+
const source = specText(record.spec, "source").toLowerCase();
|
|
143
|
+
return `${record.agentName.toLowerCase()}|${record.kind}|${filePath}|${ownerEmail}|${source}`;
|
|
144
|
+
}
|
|
131
145
|
function writeRecord(locator, record) {
|
|
132
146
|
fs.mkdirSync(operationsDir(locator.agentName, locator.agentRoot), { recursive: true });
|
|
133
147
|
fs.writeFileSync(operationPath(locator), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
@@ -160,12 +174,22 @@ function listBackgroundOperations(input) {
|
|
|
160
174
|
catch {
|
|
161
175
|
return [];
|
|
162
176
|
}
|
|
163
|
-
|
|
177
|
+
const records = entries
|
|
164
178
|
.filter((entry) => entry.endsWith(".json"))
|
|
165
179
|
.map((entry) => readRecord(path.join(dir, entry)))
|
|
166
180
|
.filter((entry) => entry !== null)
|
|
167
|
-
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))
|
|
168
|
-
|
|
181
|
+
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt));
|
|
182
|
+
const visibleKeys = new Set();
|
|
183
|
+
const visible = records.filter((record) => {
|
|
184
|
+
const key = visibleOperationKey(record);
|
|
185
|
+
if (!key)
|
|
186
|
+
return true;
|
|
187
|
+
if (visibleKeys.has(key))
|
|
188
|
+
return false;
|
|
189
|
+
visibleKeys.add(key);
|
|
190
|
+
return true;
|
|
191
|
+
});
|
|
192
|
+
return visible.slice(0, input.limit ?? 10);
|
|
169
193
|
}
|
|
170
194
|
function startBackgroundOperation(input) {
|
|
171
195
|
return writeRecord(input, {
|
package/dist/heart/core.js
CHANGED
|
@@ -182,6 +182,13 @@ function getProviderDisplayLabel(facing = "human") {
|
|
|
182
182
|
};
|
|
183
183
|
return providerLabelBuilders[provider]();
|
|
184
184
|
}
|
|
185
|
+
function hasFreshPendingWork(options) {
|
|
186
|
+
const pendingMessages = options?.pendingMessages;
|
|
187
|
+
if (!Array.isArray(pendingMessages))
|
|
188
|
+
return false;
|
|
189
|
+
return pendingMessages.some((message) => typeof message?.content === "string"
|
|
190
|
+
&& message.content.trim().length > 0);
|
|
191
|
+
}
|
|
185
192
|
// Sole-call tools must be the only tool call in a turn. When they appear
|
|
186
193
|
// alongside other tools, the sole-call tool is rejected with this message.
|
|
187
194
|
const SOLE_CALL_REJECTION = {
|
|
@@ -891,6 +898,14 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
891
898
|
providerRuntime.appendToolOutput(result.toolCalls[0].id, gateMessage);
|
|
892
899
|
continue;
|
|
893
900
|
}
|
|
901
|
+
if (hasFreshPendingWork(options)) {
|
|
902
|
+
callbacks.onToolEnd("rest", (0, tools_1.summarizeArgs)("rest", restArgs), false);
|
|
903
|
+
messages.push(msg);
|
|
904
|
+
const gateMessage = "fresh work arrived for me this turn — inspect the pending messages above and take the next concrete action before you rest.";
|
|
905
|
+
messages.push({ role: "tool", tool_call_id: result.toolCalls[0].id, content: gateMessage });
|
|
906
|
+
providerRuntime.appendToolOutput(result.toolCalls[0].id, gateMessage);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
894
909
|
callbacks.onToolEnd("rest", (0, tools_1.summarizeArgs)("rest", restArgs), true);
|
|
895
910
|
messages.push(msg);
|
|
896
911
|
const ack = "(resting)";
|
|
@@ -44,6 +44,7 @@ exports.mergeStartupStability = mergeStartupStability;
|
|
|
44
44
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
45
45
|
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
46
46
|
exports.checkManualCloneBundles = checkManualCloneBundles;
|
|
47
|
+
exports.resolveMailImportFilePath = resolveMailImportFilePath;
|
|
47
48
|
exports.runOuroCli = runOuroCli;
|
|
48
49
|
const child_process_1 = require("child_process");
|
|
49
50
|
const crypto_1 = require("crypto");
|
|
@@ -84,6 +85,9 @@ const mbox_import_1 = require("../../mailroom/mbox-import");
|
|
|
84
85
|
const reader_1 = require("../../mailroom/reader");
|
|
85
86
|
const pending_1 = require("../../mind/pending");
|
|
86
87
|
const background_operations_1 = require("../background-operations");
|
|
88
|
+
const mail_import_discovery_1 = require("../mail-import-discovery");
|
|
89
|
+
const session_activity_1 = require("../session-activity");
|
|
90
|
+
const target_resolution_1 = require("../target-resolution");
|
|
87
91
|
const cli_parse_1 = require("./cli-parse");
|
|
88
92
|
const cli_parse_2 = require("./cli-parse");
|
|
89
93
|
const cli_help_1 = require("./cli-help");
|
|
@@ -3409,7 +3413,7 @@ function makeBackgroundOperationId(kind) {
|
|
|
3409
3413
|
const prefix = kind === "mail.import-mbox" ? "op_mail_import" : "op_mail_backfill";
|
|
3410
3414
|
return `${prefix}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
|
|
3411
3415
|
}
|
|
3412
|
-
function notifyMailOperation(agentName, record, deps) {
|
|
3416
|
+
async function notifyMailOperation(agentName, record, deps) {
|
|
3413
3417
|
const lines = [
|
|
3414
3418
|
"[Background Operation]",
|
|
3415
3419
|
`${record.title} ${record.status === "failed" ? "failed" : "finished"}.`,
|
|
@@ -3419,22 +3423,68 @@ function notifyMailOperation(agentName, record, deps) {
|
|
|
3419
3423
|
`detail: ${record.detail}`,
|
|
3420
3424
|
...(record.error?.message ? [`error: ${record.error.message}`] : []),
|
|
3421
3425
|
...(record.remediation && record.remediation.length > 0 ? ["", "next steps:", ...record.remediation.map((step) => `- ${step}`)] : []),
|
|
3426
|
+
...(record.kind === "mail.import-mbox" && record.status === "succeeded"
|
|
3427
|
+
? ["", "do not rerun the same archive unless a newer export appears."]
|
|
3428
|
+
: []),
|
|
3422
3429
|
"",
|
|
3423
3430
|
"Use query_active_work for the live background-work view before drilling into mail details.",
|
|
3424
3431
|
];
|
|
3425
|
-
|
|
3432
|
+
const agentRoot = providerCliAgentRoot({ agent: agentName }, deps);
|
|
3433
|
+
const content = lines.join("\n");
|
|
3434
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", "self", "inner", "dialog"), {
|
|
3426
3435
|
from: "mailroom",
|
|
3427
3436
|
friendId: "self",
|
|
3428
3437
|
channel: "inner",
|
|
3429
3438
|
key: "dialog",
|
|
3430
|
-
content
|
|
3439
|
+
content,
|
|
3431
3440
|
timestamp: deps.now?.() ?? Date.now(),
|
|
3432
3441
|
mode: "reflect",
|
|
3433
3442
|
});
|
|
3434
|
-
|
|
3443
|
+
await queueMailOperationTargetNotification(agentName, content, deps).catch(() => undefined);
|
|
3444
|
+
await deps.sendCommand(deps.socketPath, { kind: "inner.wake", agent: agentName })
|
|
3435
3445
|
.then(() => undefined)
|
|
3436
3446
|
.catch(() => undefined);
|
|
3437
3447
|
}
|
|
3448
|
+
async function queueMailOperationTargetNotification(agentName, content, deps) {
|
|
3449
|
+
const agentRoot = providerCliAgentRoot({ agent: agentName }, deps);
|
|
3450
|
+
const friendsDir = path.join(agentRoot, "friends");
|
|
3451
|
+
const sessionsDir = path.join(agentRoot, "state", "sessions");
|
|
3452
|
+
const mcpTarget = (0, session_activity_1.listSessionActivity)({
|
|
3453
|
+
sessionsDir,
|
|
3454
|
+
friendsDir,
|
|
3455
|
+
agentName,
|
|
3456
|
+
}).find((candidate) => candidate.channel === "mcp");
|
|
3457
|
+
if (mcpTarget) {
|
|
3458
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", mcpTarget.friendId, mcpTarget.channel, mcpTarget.key), {
|
|
3459
|
+
from: "mailroom",
|
|
3460
|
+
friendId: mcpTarget.friendId,
|
|
3461
|
+
channel: mcpTarget.channel,
|
|
3462
|
+
key: mcpTarget.key,
|
|
3463
|
+
content,
|
|
3464
|
+
timestamp: deps.now?.() ?? Date.now(),
|
|
3465
|
+
mode: "relay",
|
|
3466
|
+
});
|
|
3467
|
+
return;
|
|
3468
|
+
}
|
|
3469
|
+
const candidates = await (0, target_resolution_1.listTargetSessionCandidates)({
|
|
3470
|
+
sessionsDir,
|
|
3471
|
+
friendsDir,
|
|
3472
|
+
agentName,
|
|
3473
|
+
friendStore: new store_file_1.FileFriendStore(friendsDir),
|
|
3474
|
+
});
|
|
3475
|
+
const target = candidates.find((candidate) => candidate.delivery.mode !== "blocked");
|
|
3476
|
+
if (!target)
|
|
3477
|
+
return;
|
|
3478
|
+
(0, pending_1.queuePendingMessage)(path.join(agentRoot, "state", "pending", target.friendId, target.channel, target.key), {
|
|
3479
|
+
from: "mailroom",
|
|
3480
|
+
friendId: target.friendId,
|
|
3481
|
+
channel: target.channel,
|
|
3482
|
+
key: target.key,
|
|
3483
|
+
content,
|
|
3484
|
+
timestamp: deps.now?.() ?? Date.now(),
|
|
3485
|
+
mode: "relay",
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3438
3488
|
function ensureTrackedMailOperation(input) {
|
|
3439
3489
|
if (!input.operationId)
|
|
3440
3490
|
return null;
|
|
@@ -3543,17 +3593,108 @@ async function launchBackgroundMailCommand(command, deps, args, title, queuedSum
|
|
|
3543
3593
|
`operation: ${operationId}`,
|
|
3544
3594
|
...(spec ? Object.entries(spec).map(([key, value]) => `${key}: ${String(value)}`) : []),
|
|
3545
3595
|
`${command.agent} will be woken on failure or completion.`,
|
|
3546
|
-
"Use query_active_work
|
|
3596
|
+
"Use query_active_work only when a live status check is actually needed.",
|
|
3547
3597
|
].join("\n");
|
|
3548
3598
|
deps.writeStdout(message);
|
|
3549
3599
|
return message;
|
|
3550
3600
|
}
|
|
3601
|
+
function buildMailImportOperationSpec(filePath, ownerEmail, source) {
|
|
3602
|
+
const stats = fs.statSync(filePath);
|
|
3603
|
+
return {
|
|
3604
|
+
filePath,
|
|
3605
|
+
...(ownerEmail ? { ownerEmail } : {}),
|
|
3606
|
+
...(source ? { source } : {}),
|
|
3607
|
+
fileSizeBytes: stats.size,
|
|
3608
|
+
fileModifiedAt: new Date(stats.mtimeMs).toISOString(),
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3611
|
+
function matchingMailImportOperation(record, command, filePath) {
|
|
3612
|
+
if (record.kind !== "mail.import-mbox")
|
|
3613
|
+
return false;
|
|
3614
|
+
if ((record.spec?.filePath ?? null) !== filePath)
|
|
3615
|
+
return false;
|
|
3616
|
+
const expectedOwnerEmail = command.ownerEmail?.trim() ?? "";
|
|
3617
|
+
const expectedSource = command.source?.trim() ?? "";
|
|
3618
|
+
const recordOwnerEmail = typeof record.spec?.ownerEmail === "string" ? record.spec.ownerEmail.trim() : "";
|
|
3619
|
+
const recordSource = typeof record.spec?.source === "string" ? record.spec.source.trim() : "";
|
|
3620
|
+
return recordOwnerEmail === expectedOwnerEmail && recordSource === expectedSource;
|
|
3621
|
+
}
|
|
3622
|
+
function latestComparableOperationTimestamp(record) {
|
|
3623
|
+
const candidates = [record.finishedAt, record.updatedAt];
|
|
3624
|
+
for (const candidate of candidates) {
|
|
3625
|
+
if (typeof candidate !== "string")
|
|
3626
|
+
continue;
|
|
3627
|
+
const parsed = Date.parse(candidate);
|
|
3628
|
+
if (Number.isFinite(parsed))
|
|
3629
|
+
return parsed;
|
|
3630
|
+
}
|
|
3631
|
+
return null;
|
|
3632
|
+
}
|
|
3633
|
+
function duplicateMailImportMessage(command, filePath, record) {
|
|
3634
|
+
const lines = [
|
|
3635
|
+
`Background mail import already ${record.status === "succeeded" ? "completed" : record.status} for ${command.agent}`,
|
|
3636
|
+
`operation: ${record.id}`,
|
|
3637
|
+
`filePath: ${filePath}`,
|
|
3638
|
+
...(command.ownerEmail ? [`ownerEmail: ${command.ownerEmail}`] : []),
|
|
3639
|
+
...(command.source ? [`source: ${command.source}`] : []),
|
|
3640
|
+
`summary: ${record.summary}`,
|
|
3641
|
+
...(record.detail ? [`detail: ${record.detail}`] : []),
|
|
3642
|
+
];
|
|
3643
|
+
if (record.status === "succeeded") {
|
|
3644
|
+
lines.push("This archive has not changed since that import. Download a newer export before rerunning.");
|
|
3645
|
+
}
|
|
3646
|
+
else {
|
|
3647
|
+
lines.push(`${command.agent} will be woken on failure or completion.`);
|
|
3648
|
+
}
|
|
3649
|
+
return lines.join("\n");
|
|
3650
|
+
}
|
|
3651
|
+
function findExistingMailImportOperation(command, deps, filePath) {
|
|
3652
|
+
const agentRoot = providerCliAgentRoot({ agent: command.agent }, deps);
|
|
3653
|
+
const currentFileMtimeMs = fs.statSync(filePath).mtimeMs;
|
|
3654
|
+
const operations = (0, background_operations_1.listBackgroundOperations)({ agentName: command.agent, agentRoot, limit: 50 });
|
|
3655
|
+
for (const record of operations) {
|
|
3656
|
+
if (!matchingMailImportOperation(record, command, filePath))
|
|
3657
|
+
continue;
|
|
3658
|
+
if (record.status === "queued" || record.status === "running") {
|
|
3659
|
+
return record;
|
|
3660
|
+
}
|
|
3661
|
+
if (record.status === "succeeded") {
|
|
3662
|
+
const recordTimestamp = latestComparableOperationTimestamp(record);
|
|
3663
|
+
if (recordTimestamp !== null && currentFileMtimeMs <= recordTimestamp) {
|
|
3664
|
+
return record;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
return null;
|
|
3669
|
+
}
|
|
3670
|
+
function resolveMailImportFilePath(command, deps) {
|
|
3671
|
+
if (command.filePath) {
|
|
3672
|
+
return path.resolve(command.filePath);
|
|
3673
|
+
}
|
|
3674
|
+
if (!command.discover) {
|
|
3675
|
+
throw new Error("mail import requires either --file <path> or --discover");
|
|
3676
|
+
}
|
|
3677
|
+
return (0, mail_import_discovery_1.discoverMailImportFilePath)({
|
|
3678
|
+
agentName: command.agent,
|
|
3679
|
+
ownerEmail: command.ownerEmail,
|
|
3680
|
+
source: command.source,
|
|
3681
|
+
repoRoot: path.resolve(deps.getRepoCwd?.() ?? process.cwd()),
|
|
3682
|
+
homeDir: deps.homeDir ?? os.homedir(),
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3551
3685
|
async function executeMailImportMbox(command, deps) {
|
|
3552
|
-
const filePath =
|
|
3686
|
+
const filePath = resolveMailImportFilePath(command, deps);
|
|
3553
3687
|
if (!command.foreground) {
|
|
3554
3688
|
if (!fs.existsSync(filePath)) {
|
|
3555
3689
|
throw new Error(`no such file: ${filePath}`);
|
|
3556
3690
|
}
|
|
3691
|
+
const duplicate = findExistingMailImportOperation(command, deps, filePath);
|
|
3692
|
+
if (duplicate) {
|
|
3693
|
+
const message = duplicateMailImportMessage(command, filePath, duplicate);
|
|
3694
|
+
deps.writeStdout(message);
|
|
3695
|
+
return message;
|
|
3696
|
+
}
|
|
3697
|
+
const spec = buildMailImportOperationSpec(filePath, command.ownerEmail, command.source);
|
|
3557
3698
|
return launchBackgroundMailCommand(command, deps, [
|
|
3558
3699
|
"mail",
|
|
3559
3700
|
"import-mbox",
|
|
@@ -3563,13 +3704,10 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3563
3704
|
filePath,
|
|
3564
3705
|
...(command.ownerEmail ? ["--owner-email", command.ownerEmail] : []),
|
|
3565
3706
|
...(command.source ? ["--source", command.source] : []),
|
|
3566
|
-
], "mail import", "queued delegated mail import",
|
|
3567
|
-
filePath,
|
|
3568
|
-
...(command.ownerEmail ? { ownerEmail: command.ownerEmail } : {}),
|
|
3569
|
-
...(command.source ? { source: command.source } : {}),
|
|
3570
|
-
});
|
|
3707
|
+
], "mail import", "queued delegated mail import", spec);
|
|
3571
3708
|
}
|
|
3572
3709
|
const progress = createHumanCommandProgress(deps, "mail import");
|
|
3710
|
+
const spec = buildMailImportOperationSpec(filePath, command.ownerEmail, command.source);
|
|
3573
3711
|
const trackedOperation = ensureTrackedMailOperation({
|
|
3574
3712
|
agentName: command.agent,
|
|
3575
3713
|
deps,
|
|
@@ -3577,11 +3715,7 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3577
3715
|
kind: "mail.import-mbox",
|
|
3578
3716
|
title: "mail import",
|
|
3579
3717
|
queuedSummary: "queued delegated mail import",
|
|
3580
|
-
spec
|
|
3581
|
-
filePath,
|
|
3582
|
-
...(command.ownerEmail ? { ownerEmail: command.ownerEmail } : {}),
|
|
3583
|
-
...(command.source ? { source: command.source } : {}),
|
|
3584
|
-
},
|
|
3718
|
+
spec,
|
|
3585
3719
|
});
|
|
3586
3720
|
try {
|
|
3587
3721
|
trackedOperation?.running("reading Mailroom config", `file: ${filePath}`, {
|
|
@@ -3643,6 +3777,7 @@ async function executeMailImportMbox(command, deps) {
|
|
|
3643
3777
|
progress.completePhase("reading Mailroom config", "imported");
|
|
3644
3778
|
progress.end();
|
|
3645
3779
|
const message = [
|
|
3780
|
+
...(command.discover ? [`Discovered MBOX: ${filePath}`] : []),
|
|
3646
3781
|
`Imported MBOX for ${command.agent}`,
|
|
3647
3782
|
`file: ${filePath}`,
|
|
3648
3783
|
`grant: ${result.sourceGrant.grantId} (${result.sourceGrant.source}; ${result.sourceGrant.ownerEmail})`,
|
|
@@ -6538,14 +6673,13 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6538
6673
|
/* v8 ignore start -- inner status handler: requires real agent state on disk @preserve */
|
|
6539
6674
|
if (command.kind === "inner.status") {
|
|
6540
6675
|
try {
|
|
6541
|
-
const agentRoot = (0, identity_1.getAgentRoot)(command.agent);
|
|
6676
|
+
const agentRoot = deps.agentBundleRoot ?? (0, identity_1.getAgentRoot)(command.agent);
|
|
6542
6677
|
const { buildInnerStatusOutput } = await Promise.resolve().then(() => __importStar(require("./inner-status")));
|
|
6543
|
-
const { sessionPath: getSessionPath } = await Promise.resolve().then(() => __importStar(require("../config")));
|
|
6544
6678
|
const { parseCadenceToMs: parseCadenceMs, DEFAULT_CADENCE_MS } = await Promise.resolve().then(() => __importStar(require("./cadence")));
|
|
6545
6679
|
const { parseFrontmatter } = await Promise.resolve().then(() => __importStar(require("../../repertoire/tasks/parser")));
|
|
6546
6680
|
const { listActiveReturnObligations } = await Promise.resolve().then(() => __importStar(require("../../arc/obligations")));
|
|
6547
6681
|
// Read runtime state
|
|
6548
|
-
const innerSessionPath =
|
|
6682
|
+
const innerSessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
6549
6683
|
const runtimeJsonPath = path.join(path.dirname(innerSessionPath), "runtime.json");
|
|
6550
6684
|
let runtimeState = null;
|
|
6551
6685
|
try {
|
|
@@ -79,9 +79,15 @@ exports.COMMAND_REGISTRY = {
|
|
|
79
79
|
usage: "ouro doctor",
|
|
80
80
|
example: "ouro doctor",
|
|
81
81
|
},
|
|
82
|
+
mailbox: {
|
|
83
|
+
category: "Agents",
|
|
84
|
+
description: "Show the agent's current mailbox overview",
|
|
85
|
+
usage: "ouro mailbox [--json]",
|
|
86
|
+
example: "ouro mailbox --json",
|
|
87
|
+
},
|
|
82
88
|
outlook: {
|
|
83
89
|
category: "Agents",
|
|
84
|
-
description: "
|
|
90
|
+
description: "Deprecated alias for `ouro mailbox`",
|
|
85
91
|
usage: "ouro outlook [--json]",
|
|
86
92
|
example: "ouro outlook --json",
|
|
87
93
|
},
|
|
@@ -181,7 +187,7 @@ exports.COMMAND_REGISTRY = {
|
|
|
181
187
|
category: "Auth",
|
|
182
188
|
description: "Import delegated mail and repair hosted Mailroom mailbox indexes",
|
|
183
189
|
usage: "ouro mail <import-mbox|backfill-indexes> [--agent <name>]",
|
|
184
|
-
example: "ouro mail import-mbox --
|
|
190
|
+
example: "ouro mail import-mbox --discover --owner-email ari@mendelow.me --source hey --agent slugger",
|
|
185
191
|
subcommands: ["import-mbox", "backfill-indexes"],
|
|
186
192
|
},
|
|
187
193
|
use: {
|
|
@@ -323,8 +329,8 @@ const SUBCOMMAND_HELP = {
|
|
|
323
329
|
},
|
|
324
330
|
"mail import-mbox": {
|
|
325
331
|
description: "Import a HEY or other MBOX export into an existing delegated Mailroom source grant",
|
|
326
|
-
usage: "ouro mail import-mbox --file <path
|
|
327
|
-
example: "ouro mail import-mbox --
|
|
332
|
+
usage: "ouro mail import-mbox (--file <path>|--discover) [--owner-email <email>] [--source <label>] [--agent <name>] [--foreground]",
|
|
333
|
+
example: "ouro mail import-mbox --discover --owner-email ari@mendelow.me --source hey --agent slugger",
|
|
328
334
|
},
|
|
329
335
|
"mail backfill-indexes": {
|
|
330
336
|
description: "Rebuild hosted blob mailbox indexes for faster recent-mail reads after large legacy imports or drift repair.",
|
|
@@ -79,7 +79,7 @@ function usage() {
|
|
|
79
79
|
" ouro check [--agent <name>] --lane outward|inner",
|
|
80
80
|
" ouro repair [--agent <name>]",
|
|
81
81
|
" ouro provider refresh [--agent <name>]",
|
|
82
|
-
" ouro
|
|
82
|
+
" ouro mailbox [--json]",
|
|
83
83
|
" ouro -v|--version",
|
|
84
84
|
" ouro config model [--agent <name>] <model-name>",
|
|
85
85
|
" ouro config models [--agent <name>]",
|
|
@@ -885,7 +885,7 @@ function parseConnectCommand(args) {
|
|
|
885
885
|
}
|
|
886
886
|
function parseMailCommand(args) {
|
|
887
887
|
const [sub, ...subArgs] = args;
|
|
888
|
-
const usageText = "Usage: ouro mail import-mbox --file <path
|
|
888
|
+
const usageText = "Usage: ouro mail import-mbox (--file <path>|--discover) [--owner-email <email>] [--source <label>] [--agent <name>] [--foreground]\n ouro mail backfill-indexes [--agent <name>] [--foreground]";
|
|
889
889
|
if (sub === "backfill-indexes") {
|
|
890
890
|
const { agent, rest } = extractAgentFlag(subArgs);
|
|
891
891
|
let foreground = false;
|
|
@@ -914,6 +914,7 @@ function parseMailCommand(args) {
|
|
|
914
914
|
}
|
|
915
915
|
const { agent, rest } = extractAgentFlag(subArgs);
|
|
916
916
|
let filePath;
|
|
917
|
+
let discover = false;
|
|
917
918
|
let ownerEmail;
|
|
918
919
|
let source;
|
|
919
920
|
let foreground = false;
|
|
@@ -924,6 +925,10 @@ function parseMailCommand(args) {
|
|
|
924
925
|
filePath = rest[++i];
|
|
925
926
|
continue;
|
|
926
927
|
}
|
|
928
|
+
if (token === "--discover") {
|
|
929
|
+
discover = true;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
927
932
|
if (token === "--owner-email" && rest[i + 1]) {
|
|
928
933
|
ownerEmail = rest[++i];
|
|
929
934
|
continue;
|
|
@@ -942,13 +947,14 @@ function parseMailCommand(args) {
|
|
|
942
947
|
}
|
|
943
948
|
throw new Error(usageText);
|
|
944
949
|
}
|
|
945
|
-
if (
|
|
950
|
+
if ((filePath ? 1 : 0) + (discover ? 1 : 0) !== 1) {
|
|
946
951
|
throw new Error(usageText);
|
|
947
952
|
}
|
|
948
953
|
return {
|
|
949
954
|
kind: "mail.import-mbox",
|
|
950
955
|
...(agent ? { agent } : {}),
|
|
951
|
-
filePath,
|
|
956
|
+
...(filePath ? { filePath } : {}),
|
|
957
|
+
...(discover ? { discover: true } : {}),
|
|
952
958
|
...(ownerEmail ? { ownerEmail } : {}),
|
|
953
959
|
...(source ? { source } : {}),
|
|
954
960
|
...(foreground ? { foreground: true } : {}),
|
|
@@ -1452,7 +1458,7 @@ function parseOuroCommand(args) {
|
|
|
1452
1458
|
return { kind: "daemon.logs.prune" };
|
|
1453
1459
|
return { kind: "daemon.logs" };
|
|
1454
1460
|
}
|
|
1455
|
-
if (head === "outlook")
|
|
1461
|
+
if (head === "mailbox" || head === "outlook")
|
|
1456
1462
|
return { kind: "outlook", ...(args.includes("--json") ? { json: true } : {}) };
|
|
1457
1463
|
if (head === "hatch")
|
|
1458
1464
|
return parseHatchCommand(args.slice(1));
|
|
@@ -308,7 +308,7 @@ function formatDaemonStatusOutput(response, fallback) {
|
|
|
308
308
|
// ── Key-value overview ──
|
|
309
309
|
const kvLine = (label, value) => ` ${teal(label.padEnd(11))} ${value}`;
|
|
310
310
|
lines.push(kvLine("Socket", ov.socketPath));
|
|
311
|
-
lines.push(kvLine("
|
|
311
|
+
lines.push(kvLine("Mailbox", ov.outlookUrl));
|
|
312
312
|
lines.push(kvLine("Health", `${statusDot(ov.health)} ${ov.health}`));
|
|
313
313
|
lines.push(kvLine("Updated", ov.lastUpdated));
|
|
314
314
|
lines.push("");
|
|
@@ -48,6 +48,7 @@ exports.readInnerDialogRawData = readInnerDialogRawData;
|
|
|
48
48
|
exports.followThoughts = followThoughts;
|
|
49
49
|
const fs = __importStar(require("fs"));
|
|
50
50
|
const path = __importStar(require("path"));
|
|
51
|
+
const session_events_1 = require("../session-events");
|
|
51
52
|
const runtime_1 = require("../../nerves/runtime");
|
|
52
53
|
function contentToText(content) {
|
|
53
54
|
if (typeof content === "string")
|
|
@@ -364,21 +365,34 @@ function parseInnerDialogSession(sessionPath) {
|
|
|
364
365
|
catch {
|
|
365
366
|
return [];
|
|
366
367
|
}
|
|
367
|
-
let
|
|
368
|
+
let parsedRaw;
|
|
368
369
|
try {
|
|
369
|
-
|
|
370
|
+
parsedRaw = JSON.parse(raw);
|
|
370
371
|
}
|
|
371
372
|
catch {
|
|
372
373
|
return [];
|
|
373
374
|
}
|
|
374
|
-
|
|
375
|
+
const legacyRecord = parsedRaw && typeof parsedRaw === "object"
|
|
376
|
+
? parsedRaw
|
|
377
|
+
: null;
|
|
378
|
+
const messages = legacyRecord?.version === 1 && Array.isArray(legacyRecord.messages)
|
|
379
|
+
? legacyRecord.messages
|
|
380
|
+
: (() => {
|
|
381
|
+
const envelope = (0, session_events_1.parseSessionEnvelope)(parsedRaw, {
|
|
382
|
+
recordedAt: new Date().toISOString(),
|
|
383
|
+
});
|
|
384
|
+
if (!envelope)
|
|
385
|
+
return null;
|
|
386
|
+
return (0, session_events_1.projectProviderMessages)(envelope);
|
|
387
|
+
})();
|
|
388
|
+
if (!messages)
|
|
375
389
|
return [];
|
|
390
|
+
const normalizedMessages = messages;
|
|
376
391
|
const turns = [];
|
|
377
|
-
const messages = data.messages;
|
|
378
392
|
// Walk messages, pairing user → (tool calls) → assistant sequences
|
|
379
393
|
let i = 0;
|
|
380
|
-
while (i <
|
|
381
|
-
const msg =
|
|
394
|
+
while (i < normalizedMessages.length) {
|
|
395
|
+
const msg = normalizedMessages[i];
|
|
382
396
|
if (msg.role === "system") {
|
|
383
397
|
i++;
|
|
384
398
|
continue;
|
|
@@ -392,8 +406,8 @@ function parseInnerDialogSession(sessionPath) {
|
|
|
392
406
|
// Collect all messages until the next user message (or end)
|
|
393
407
|
const turnMessages = [];
|
|
394
408
|
let j = i + 1;
|
|
395
|
-
while (j <
|
|
396
|
-
turnMessages.push(
|
|
409
|
+
while (j < normalizedMessages.length && normalizedMessages[j].role !== "user") {
|
|
410
|
+
turnMessages.push(normalizedMessages[j]);
|
|
397
411
|
j++;
|
|
398
412
|
}
|
|
399
413
|
// Find the last assistant text response in this turn.
|