@ouro.bot/cli 0.1.0-alpha.482 → 0.1.0-alpha.484
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 +14 -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 +104 -15
- 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/provider-failover.js +35 -0
- package/dist/heart/session-events.js +91 -5
- package/dist/heart/turn-context.js +11 -0
- 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 +70 -7
- package/package.json +1 -1
|
@@ -0,0 +1,318 @@
|
|
|
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.defaultMailImportDiscoveryDirs = defaultMailImportDiscoveryDirs;
|
|
37
|
+
exports.listDiscoveredMboxCandidates = listDiscoveredMboxCandidates;
|
|
38
|
+
exports.rankDiscoveredMboxCandidates = rankDiscoveredMboxCandidates;
|
|
39
|
+
exports.discoverMailImportFilePath = discoverMailImportFilePath;
|
|
40
|
+
exports.listAmbientMailImportOperations = listAmbientMailImportOperations;
|
|
41
|
+
exports.listVisibleBackgroundOperations = listVisibleBackgroundOperations;
|
|
42
|
+
const fs = __importStar(require("node:fs"));
|
|
43
|
+
const os = __importStar(require("node:os"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const background_operations_1 = require("./background-operations");
|
|
46
|
+
const identity = __importStar(require("./identity"));
|
|
47
|
+
const DEFAULT_RECENT_IMPORT_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
48
|
+
const DEFAULT_VISIBLE_BACKGROUND_OPERATION_LIMIT = 5;
|
|
49
|
+
const DEFAULT_VISIBLE_IMPORT_CANDIDATE_LIMIT = 3;
|
|
50
|
+
const DEFAULT_WORKTREE_POOL_SEARCH_DEPTH = 2;
|
|
51
|
+
const DEFAULT_AGENT_WORKSPACE_SCAN_LIMIT = 20;
|
|
52
|
+
const SKIPPED_HOME_SCAN_DIRS = new Set([
|
|
53
|
+
".Trash",
|
|
54
|
+
"Applications",
|
|
55
|
+
"Library",
|
|
56
|
+
"Movies",
|
|
57
|
+
"Music",
|
|
58
|
+
"Pictures",
|
|
59
|
+
"Public",
|
|
60
|
+
"node_modules",
|
|
61
|
+
]);
|
|
62
|
+
function sortBackgroundOperationsNewestFirst(left, right) {
|
|
63
|
+
return Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
|
|
64
|
+
}
|
|
65
|
+
function recentImportFingerprint(candidates) {
|
|
66
|
+
return candidates
|
|
67
|
+
.map((candidate) => `${candidate.path}:${candidate.mtimeMs}`)
|
|
68
|
+
.sort((left, right) => left.localeCompare(right))
|
|
69
|
+
.join("|");
|
|
70
|
+
}
|
|
71
|
+
function specText(spec, key) {
|
|
72
|
+
const value = spec?.[key];
|
|
73
|
+
return typeof value === "string" ? value.trim() : "";
|
|
74
|
+
}
|
|
75
|
+
function latestComparableOperationTimestamp(record) {
|
|
76
|
+
const fileModifiedAt = specText(record.spec, "fileModifiedAt");
|
|
77
|
+
if (fileModifiedAt) {
|
|
78
|
+
const parsed = Date.parse(fileModifiedAt);
|
|
79
|
+
if (Number.isFinite(parsed))
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
const candidates = [record.finishedAt, record.updatedAt];
|
|
83
|
+
for (const candidate of candidates) {
|
|
84
|
+
if (typeof candidate !== "string")
|
|
85
|
+
continue;
|
|
86
|
+
const parsed = Date.parse(candidate);
|
|
87
|
+
if (Number.isFinite(parsed))
|
|
88
|
+
return parsed;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function candidateAlreadyCoveredByOperation(candidate, operation) {
|
|
93
|
+
if (operation.kind !== "mail.import-mbox")
|
|
94
|
+
return false;
|
|
95
|
+
if (specText(operation.spec, "filePath") !== candidate.path)
|
|
96
|
+
return false;
|
|
97
|
+
if (operation.status !== "succeeded")
|
|
98
|
+
return false;
|
|
99
|
+
const operationTimestamp = latestComparableOperationTimestamp(operation);
|
|
100
|
+
return operationTimestamp !== null && candidate.mtimeMs <= operationTimestamp;
|
|
101
|
+
}
|
|
102
|
+
function listChildDirs(dir) {
|
|
103
|
+
if (!fs.existsSync(dir))
|
|
104
|
+
return [];
|
|
105
|
+
try {
|
|
106
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
107
|
+
.filter((entry) => entry.isDirectory())
|
|
108
|
+
.map((entry) => path.join(dir, entry.name));
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function findWorktreePools(rootDir, maxDepth) {
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
const found = [];
|
|
117
|
+
function visit(currentDir, depth) {
|
|
118
|
+
if (depth > maxDepth || seen.has(currentDir))
|
|
119
|
+
return;
|
|
120
|
+
seen.add(currentDir);
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
if (!entry.isDirectory())
|
|
130
|
+
continue;
|
|
131
|
+
if (entry.name.startsWith(".") && entry.name !== ".playwright-mcp")
|
|
132
|
+
continue;
|
|
133
|
+
if (SKIPPED_HOME_SCAN_DIRS.has(entry.name))
|
|
134
|
+
continue;
|
|
135
|
+
const childDir = path.join(currentDir, entry.name);
|
|
136
|
+
if (entry.name === "_worktrees") {
|
|
137
|
+
found.push(childDir);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
visit(childDir, depth + 1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
visit(rootDir, 0);
|
|
144
|
+
return found;
|
|
145
|
+
}
|
|
146
|
+
function listAgentWorkspaceSandboxDirs(agentName) {
|
|
147
|
+
if (!agentName)
|
|
148
|
+
return [];
|
|
149
|
+
const getAgentRepoWorkspacesRoot = Object.getOwnPropertyDescriptor(identity, "getAgentRepoWorkspacesRoot")?.value;
|
|
150
|
+
const workspacesRoot = typeof getAgentRepoWorkspacesRoot === "function"
|
|
151
|
+
? getAgentRepoWorkspacesRoot(agentName)
|
|
152
|
+
: "";
|
|
153
|
+
if (!workspacesRoot)
|
|
154
|
+
return [];
|
|
155
|
+
return listChildDirs(workspacesRoot)
|
|
156
|
+
.slice(0, DEFAULT_AGENT_WORKSPACE_SCAN_LIMIT)
|
|
157
|
+
.map((workspaceDir) => path.join(workspaceDir, ".playwright-mcp"));
|
|
158
|
+
}
|
|
159
|
+
function listWorktreePoolSandboxDirs(homeDir) {
|
|
160
|
+
return findWorktreePools(homeDir, DEFAULT_WORKTREE_POOL_SEARCH_DEPTH)
|
|
161
|
+
.flatMap((poolDir) => listChildDirs(poolDir))
|
|
162
|
+
.map((worktreeDir) => path.join(worktreeDir, ".playwright-mcp"));
|
|
163
|
+
}
|
|
164
|
+
function defaultMailImportDiscoveryDirs(input = {}) {
|
|
165
|
+
const repoRoot = path.resolve(input.repoRoot ?? process.cwd());
|
|
166
|
+
const homeDir = path.resolve(input.homeDir ?? os.homedir());
|
|
167
|
+
return [...new Set([
|
|
168
|
+
path.join(repoRoot, ".playwright-mcp"),
|
|
169
|
+
...listAgentWorkspaceSandboxDirs(input.agentName),
|
|
170
|
+
...listWorktreePoolSandboxDirs(homeDir),
|
|
171
|
+
path.join(homeDir, ".playwright-mcp"),
|
|
172
|
+
path.join(homeDir, "Downloads"),
|
|
173
|
+
].map((dir) => path.resolve(dir)))];
|
|
174
|
+
}
|
|
175
|
+
function listDiscoveredMboxCandidates(dir) {
|
|
176
|
+
if (!fs.existsSync(dir))
|
|
177
|
+
return [];
|
|
178
|
+
try {
|
|
179
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
180
|
+
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".mbox"))
|
|
181
|
+
.map((entry) => {
|
|
182
|
+
const candidatePath = path.join(dir, entry.name);
|
|
183
|
+
const stat = fs.statSync(candidatePath);
|
|
184
|
+
return { path: candidatePath, name: entry.name, mtimeMs: stat.mtimeMs };
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function normalizeMboxDiscoveryText(value) {
|
|
192
|
+
return value
|
|
193
|
+
.toLowerCase()
|
|
194
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
195
|
+
.trim();
|
|
196
|
+
}
|
|
197
|
+
function tokenizeMboxDiscoveryHint(value) {
|
|
198
|
+
if (!value)
|
|
199
|
+
return [];
|
|
200
|
+
return normalizeMboxDiscoveryText(value)
|
|
201
|
+
.split(" ")
|
|
202
|
+
.filter((token) => token.length > 0);
|
|
203
|
+
}
|
|
204
|
+
function scoreDiscoveredMboxCandidate(fileName, mtimeMs, ownerEmail, source) {
|
|
205
|
+
const normalized = normalizeMboxDiscoveryText(fileName);
|
|
206
|
+
const ownerTokens = tokenizeMboxDiscoveryHint(ownerEmail);
|
|
207
|
+
const sourceTokens = tokenizeMboxDiscoveryHint(source);
|
|
208
|
+
const ownerScore = ownerTokens.reduce((score, token) => score + (normalized.includes(token) ? 50 : 0), 0);
|
|
209
|
+
const sourceScore = sourceTokens.reduce((score, token) => score + (normalized.includes(token) ? 20 : 0), 0);
|
|
210
|
+
const recencyScore = Math.floor(mtimeMs / 1000);
|
|
211
|
+
return ownerScore + sourceScore + recencyScore;
|
|
212
|
+
}
|
|
213
|
+
function rankDiscoveredMboxCandidates(candidates, ownerEmail, source) {
|
|
214
|
+
return candidates
|
|
215
|
+
.map((candidate) => ({
|
|
216
|
+
path: candidate.path,
|
|
217
|
+
score: scoreDiscoveredMboxCandidate(candidate.name, candidate.mtimeMs, ownerEmail, source),
|
|
218
|
+
}))
|
|
219
|
+
.sort((left, right) => right.score - left.score);
|
|
220
|
+
}
|
|
221
|
+
function discoverMailImportFilePath(input) {
|
|
222
|
+
const searchDirs = defaultMailImportDiscoveryDirs({
|
|
223
|
+
agentName: input.agentName,
|
|
224
|
+
repoRoot: input.repoRoot,
|
|
225
|
+
homeDir: input.homeDir,
|
|
226
|
+
});
|
|
227
|
+
const candidates = searchDirs.flatMap((dir) => listDiscoveredMboxCandidates(dir));
|
|
228
|
+
const ranked = rankDiscoveredMboxCandidates(candidates, input.ownerEmail, input.source);
|
|
229
|
+
if (ranked.length === 0) {
|
|
230
|
+
throw new Error(`could not discover an MBOX file in ${searchDirs.join(", ")}`);
|
|
231
|
+
}
|
|
232
|
+
const topScore = ranked[0].score;
|
|
233
|
+
const topCandidates = ranked.filter((candidate) => candidate.score === topScore);
|
|
234
|
+
if (topCandidates.length > 1) {
|
|
235
|
+
throw new Error(`multiple candidate MBOX files found: ${topCandidates.map((candidate) => candidate.path).join(", ")}`);
|
|
236
|
+
}
|
|
237
|
+
return topCandidates[0].path;
|
|
238
|
+
}
|
|
239
|
+
function summarizeAmbientImportCandidates(candidates, candidateLimit) {
|
|
240
|
+
const visibleCandidates = candidates.slice(0, candidateLimit);
|
|
241
|
+
const hiddenCount = Math.max(0, candidates.length - visibleCandidates.length);
|
|
242
|
+
const summary = visibleCandidates.length === 1
|
|
243
|
+
? "recent MBOX archive ready for import"
|
|
244
|
+
: `${visibleCandidates.length} recent MBOX archives ready for import`;
|
|
245
|
+
const detailLines = [
|
|
246
|
+
"recent candidates:",
|
|
247
|
+
...visibleCandidates.map((candidate) => `- ${candidate.path}`),
|
|
248
|
+
...(hiddenCount > 0 ? [`- ...and ${hiddenCount} more recent archive${hiddenCount === 1 ? "" : "s"}`] : []),
|
|
249
|
+
"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
|
+
];
|
|
251
|
+
return {
|
|
252
|
+
summary,
|
|
253
|
+
detail: detailLines.join("\n"),
|
|
254
|
+
spec: {
|
|
255
|
+
fingerprint: recentImportFingerprint(candidates),
|
|
256
|
+
candidatePaths: visibleCandidates.map((candidate) => candidate.path),
|
|
257
|
+
newestCandidatePath: visibleCandidates[0]?.path ?? null,
|
|
258
|
+
newestCandidateMtime: visibleCandidates[0] ? new Date(visibleCandidates[0].mtimeMs).toISOString() : null,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function listAmbientMailImportOperations(input) {
|
|
263
|
+
const existingOperations = input.existingOperations ?? [];
|
|
264
|
+
const hasLiveImport = existingOperations.some((operation) => operation.kind === "mail.import-mbox"
|
|
265
|
+
&& (operation.status === "queued" || operation.status === "running"));
|
|
266
|
+
if (hasLiveImport)
|
|
267
|
+
return [];
|
|
268
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
269
|
+
const recentWindowMs = input.recentWindowMs ?? DEFAULT_RECENT_IMPORT_WINDOW_MS;
|
|
270
|
+
const candidateLimit = input.candidateLimit ?? DEFAULT_VISIBLE_IMPORT_CANDIDATE_LIMIT;
|
|
271
|
+
const recentCandidates = defaultMailImportDiscoveryDirs({
|
|
272
|
+
agentName: input.agentName,
|
|
273
|
+
repoRoot: input.repoRoot,
|
|
274
|
+
homeDir: input.homeDir,
|
|
275
|
+
})
|
|
276
|
+
.flatMap((dir) => listDiscoveredMboxCandidates(dir))
|
|
277
|
+
.filter((candidate) => (nowMs - candidate.mtimeMs) <= recentWindowMs)
|
|
278
|
+
.filter((candidate) => !existingOperations.some((operation) => candidateAlreadyCoveredByOperation(candidate, operation)))
|
|
279
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
280
|
+
if (recentCandidates.length === 0)
|
|
281
|
+
return [];
|
|
282
|
+
const newestCandidate = recentCandidates[0];
|
|
283
|
+
const rendered = summarizeAmbientImportCandidates(recentCandidates, candidateLimit);
|
|
284
|
+
return [{
|
|
285
|
+
schemaVersion: 1,
|
|
286
|
+
id: "ambient_mail_import_ready",
|
|
287
|
+
agentName: input.agentName,
|
|
288
|
+
kind: "mail.import-discovered",
|
|
289
|
+
title: "mail import ready",
|
|
290
|
+
status: "queued",
|
|
291
|
+
summary: rendered.summary,
|
|
292
|
+
detail: rendered.detail,
|
|
293
|
+
createdAt: new Date(newestCandidate.mtimeMs).toISOString(),
|
|
294
|
+
updatedAt: new Date(newestCandidate.mtimeMs).toISOString(),
|
|
295
|
+
spec: rendered.spec,
|
|
296
|
+
}];
|
|
297
|
+
}
|
|
298
|
+
function listVisibleBackgroundOperations(input) {
|
|
299
|
+
const limit = input.limit ?? DEFAULT_VISIBLE_BACKGROUND_OPERATION_LIMIT;
|
|
300
|
+
const persisted = (0, background_operations_1.listBackgroundOperations)({
|
|
301
|
+
agentName: input.agentName,
|
|
302
|
+
agentRoot: input.agentRoot,
|
|
303
|
+
limit,
|
|
304
|
+
});
|
|
305
|
+
const ambient = listAmbientMailImportOperations({
|
|
306
|
+
agentName: input.agentName,
|
|
307
|
+
agentRoot: input.agentRoot,
|
|
308
|
+
existingOperations: persisted,
|
|
309
|
+
repoRoot: input.repoRoot,
|
|
310
|
+
homeDir: input.homeDir,
|
|
311
|
+
nowMs: input.nowMs,
|
|
312
|
+
recentWindowMs: input.recentWindowMs,
|
|
313
|
+
candidateLimit: input.candidateLimit,
|
|
314
|
+
});
|
|
315
|
+
return [...persisted, ...ambient]
|
|
316
|
+
.sort(sortBackgroundOperationsNewestFirst)
|
|
317
|
+
.slice(0, limit);
|
|
318
|
+
}
|
|
@@ -53,7 +53,7 @@ function createOutlookHttpRequestHandler(options) {
|
|
|
53
53
|
(0, outlook_http_response_1.writeJson)(response, 404, { ok: false, error: "asset not found" });
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
if (pathname === "/outlook") {
|
|
56
|
+
if (pathname === "/outlook" || pathname === "/mailbox") {
|
|
57
57
|
response.writeHead(301, { location: "/" });
|
|
58
58
|
response.end();
|
|
59
59
|
return;
|
|
@@ -61,6 +61,10 @@ function normalizeLegacyOutlookApiPath(pathname) {
|
|
|
61
61
|
return pathname.slice("/outlook".length);
|
|
62
62
|
if (pathname === "/outlook/api")
|
|
63
63
|
return "/api";
|
|
64
|
+
if (pathname.startsWith("/mailbox/api/"))
|
|
65
|
+
return pathname.slice("/mailbox".length);
|
|
66
|
+
if (pathname === "/mailbox/api")
|
|
67
|
+
return "/api";
|
|
64
68
|
return pathname;
|
|
65
69
|
}
|
|
66
70
|
function defaultSpaDistCandidates() {
|
|
@@ -76,7 +76,7 @@ async function startOutlookHttpServer(options = {}) {
|
|
|
76
76
|
(0, runtime_1.emitNervesEvent)({
|
|
77
77
|
component: "daemon",
|
|
78
78
|
event: "daemon.outlook_http_started",
|
|
79
|
-
message: "started
|
|
79
|
+
message: "started Mailbox HTTP server",
|
|
80
80
|
meta: { origin },
|
|
81
81
|
});
|
|
82
82
|
return {
|
|
@@ -91,7 +91,7 @@ async function startOutlookHttpServer(options = {}) {
|
|
|
91
91
|
(0, runtime_1.emitNervesEvent)({
|
|
92
92
|
component: "daemon",
|
|
93
93
|
event: "daemon.outlook_http_stopped",
|
|
94
|
-
message: "stopped
|
|
94
|
+
message: "stopped Mailbox HTTP server",
|
|
95
95
|
meta: { origin },
|
|
96
96
|
});
|
|
97
97
|
},
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.OUTLOOK_DEFAULT_PORT = exports.OUTLOOK_DEFAULT_INNER_VISIBILITY = exports.OUTLOOK_RELEASE_INTERACTION_MODEL = exports.OUTLOOK_PRODUCT_NAME = void 0;
|
|
4
4
|
exports.getOutlookTranscriptMessageText = getOutlookTranscriptMessageText;
|
|
5
5
|
exports.getOutlookTranscriptTimestamp = getOutlookTranscriptTimestamp;
|
|
6
|
-
exports.OUTLOOK_PRODUCT_NAME = "Ouro
|
|
6
|
+
exports.OUTLOOK_PRODUCT_NAME = "Ouro Mailbox";
|
|
7
7
|
exports.OUTLOOK_RELEASE_INTERACTION_MODEL = "read-only";
|
|
8
8
|
exports.OUTLOOK_DEFAULT_INNER_VISIBILITY = "summary";
|
|
9
9
|
exports.OUTLOOK_DEFAULT_PORT = 6876;
|
|
@@ -94,8 +94,8 @@ function buildOutlookMachineView(input) {
|
|
|
94
94
|
totals,
|
|
95
95
|
mood: deriveMood(input.machine, input.daemon),
|
|
96
96
|
entrypoints: [
|
|
97
|
-
{ kind: "web", label: "Open
|
|
98
|
-
{ kind: "cli", label: "CLI JSON", target: "ouro
|
|
97
|
+
{ kind: "web", label: "Open Mailbox", target: input.daemon.outlookUrl },
|
|
98
|
+
{ kind: "cli", label: "CLI JSON", target: "ouro mailbox --json" },
|
|
99
99
|
],
|
|
100
100
|
},
|
|
101
101
|
agents,
|
|
@@ -162,7 +162,7 @@ function buildRecentActivity(agent) {
|
|
|
162
162
|
}
|
|
163
163
|
function buildOutlookAgentView(input) {
|
|
164
164
|
/* v8 ignore next */
|
|
165
|
-
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.outlook_view_agent", message: `building
|
|
165
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.outlook_view_agent", message: `building mailbox view for ${input.agent.agentName}`, meta: { agent: input.agent.agentName } });
|
|
166
166
|
const viewer = normalizeViewer(input.viewer);
|
|
167
167
|
return {
|
|
168
168
|
productName: outlook_types_1.OUTLOOK_PRODUCT_NAME,
|
|
@@ -115,19 +115,42 @@ function readTaskSummary(agentRoot) {
|
|
|
115
115
|
issues,
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
+
const STALE_CODING_SURFACE_WINDOW_MS = 60 * 60 * 1000;
|
|
119
|
+
function buildLiveCodingSurfaceLabels(agentRoot) {
|
|
120
|
+
return new Set(readCodingSummary(agentRoot).items
|
|
121
|
+
.filter((item) => shared_1.ACTIVE_CODING_STATUSES.has(item.status))
|
|
122
|
+
.map((item) => `${item.runner} ${item.id}`));
|
|
123
|
+
}
|
|
124
|
+
function normalizeObligationCurrentSurface(currentSurface, updatedAt, liveCodingSurfaceLabels) {
|
|
125
|
+
if (!currentSurface || currentSurface.kind !== "coding")
|
|
126
|
+
return currentSurface;
|
|
127
|
+
const liveLabel = currentSurface.label.trim();
|
|
128
|
+
if (!liveLabel)
|
|
129
|
+
return null;
|
|
130
|
+
if (liveCodingSurfaceLabels.has(liveLabel))
|
|
131
|
+
return currentSurface;
|
|
132
|
+
const updatedAtMs = Date.parse(updatedAt);
|
|
133
|
+
const recentlyTouched = Number.isFinite(updatedAtMs)
|
|
134
|
+
&& (Date.now() - updatedAtMs) <= STALE_CODING_SURFACE_WINDOW_MS;
|
|
135
|
+
return recentlyTouched ? currentSurface : null;
|
|
136
|
+
}
|
|
118
137
|
function readObligationSummary(agentRoot) {
|
|
138
|
+
const liveCodingSurfaceLabels = buildLiveCodingSurfaceLabels(agentRoot);
|
|
119
139
|
const items = (0, obligations_1.readPendingObligations)(agentRoot)
|
|
120
|
-
.map((obligation) =>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
140
|
+
.map((obligation) => {
|
|
141
|
+
const updatedAt = obligation.updatedAt ?? obligation.createdAt;
|
|
142
|
+
return {
|
|
143
|
+
id: obligation.id,
|
|
144
|
+
status: obligation.status,
|
|
145
|
+
content: obligation.content,
|
|
146
|
+
updatedAt,
|
|
147
|
+
nextAction: obligation.nextAction ?? null,
|
|
148
|
+
/* v8 ignore start */
|
|
149
|
+
origin: obligation.origin ?? null,
|
|
150
|
+
currentSurface: normalizeObligationCurrentSurface(obligation.currentSurface ?? null, updatedAt, liveCodingSurfaceLabels),
|
|
151
|
+
/* v8 ignore stop */
|
|
152
|
+
};
|
|
153
|
+
})
|
|
131
154
|
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
132
155
|
return { items };
|
|
133
156
|
}
|
|
@@ -49,6 +49,7 @@ const presence_1 = require("../../../arc/presence");
|
|
|
49
49
|
const scanner_1 = require("../../../repertoire/tasks/scanner");
|
|
50
50
|
const active_work_1 = require("../../active-work");
|
|
51
51
|
const session_activity_1 = require("../../session-activity");
|
|
52
|
+
const agent_machine_1 = require("./agent-machine");
|
|
52
53
|
function sortOpenObligations(obligations) {
|
|
53
54
|
const statusPriority = {
|
|
54
55
|
returning: 0,
|
|
@@ -185,6 +186,7 @@ function readObligationDetailView(agentRoot) {
|
|
|
185
186
|
const openObligations = obligations.filter(obligations_1.isOpenObligation);
|
|
186
187
|
const sorted = sortOpenObligations(openObligations);
|
|
187
188
|
const primary = sorted[0] ?? null;
|
|
189
|
+
const normalizedSummary = new Map((0, agent_machine_1.readObligationSummary)(agentRoot).items.map((item) => [item.id, item.currentSurface]));
|
|
188
190
|
const items = openObligations.map((ob) => ({
|
|
189
191
|
id: ob.id,
|
|
190
192
|
status: ob.status,
|
|
@@ -192,7 +194,9 @@ function readObligationDetailView(agentRoot) {
|
|
|
192
194
|
updatedAt: ob.updatedAt ?? ob.createdAt,
|
|
193
195
|
nextAction: ob.nextAction ?? null,
|
|
194
196
|
origin: ob.origin ?? null,
|
|
195
|
-
currentSurface:
|
|
197
|
+
currentSurface: normalizedSummary.has(ob.id)
|
|
198
|
+
? (normalizedSummary.get(ob.id) ?? null)
|
|
199
|
+
: (ob.currentSurface ? { kind: ob.currentSurface.kind, label: ob.currentSurface.label } : null),
|
|
196
200
|
meaning: ob.meaning ? { waitingOn: ob.meaning.waitingOn?.detail ?? null } : null,
|
|
197
201
|
isPrimary: ob.id === primary.id,
|
|
198
202
|
}));
|
|
@@ -247,7 +247,7 @@ function emitMailRead(agentName, mode, status) {
|
|
|
247
247
|
(0, runtime_1.emitNervesEvent)({
|
|
248
248
|
component: "heart",
|
|
249
249
|
event: "heart.outlook_mail_read",
|
|
250
|
-
message: "reading
|
|
250
|
+
message: "reading Mailbox mail surface",
|
|
251
251
|
meta: { agentName, mode, status },
|
|
252
252
|
});
|
|
253
253
|
}
|
|
@@ -271,7 +271,7 @@ async function readMailView(agentName) {
|
|
|
271
271
|
await resolved.store.recordAccess({
|
|
272
272
|
agentId: agentName,
|
|
273
273
|
tool: "outlook_mail_list",
|
|
274
|
-
reason: "
|
|
274
|
+
reason: "mailbox read-only mailbox",
|
|
275
275
|
});
|
|
276
276
|
const accessLog = accessEntries(await resolved.store.listAccessLog(agentName));
|
|
277
277
|
emitMailRead(agentName, "list", "ready");
|
|
@@ -325,7 +325,7 @@ async function readMailMessageView(agentName, messageId) {
|
|
|
325
325
|
agentId: agentName,
|
|
326
326
|
messageId,
|
|
327
327
|
tool: "outlook_mail_message",
|
|
328
|
-
reason: "
|
|
328
|
+
reason: "mailbox read-only message body",
|
|
329
329
|
...accessProvenance(decrypted),
|
|
330
330
|
});
|
|
331
331
|
const body = decrypted.private.text.length > OUTLOOK_MAIL_BODY_LIMIT
|
|
@@ -5,6 +5,7 @@ exports.formatReadyProviderLabel = formatReadyProviderLabel;
|
|
|
5
5
|
exports.buildFailoverContext = buildFailoverContext;
|
|
6
6
|
exports.handleFailoverReply = handleFailoverReply;
|
|
7
7
|
exports.runMachineProviderFailoverInventory = runMachineProviderFailoverInventory;
|
|
8
|
+
exports.validateFailoverSwitchCandidate = validateFailoverSwitchCandidate;
|
|
8
9
|
const identity_1 = require("./identity");
|
|
9
10
|
const provider_ping_1 = require("./provider-ping");
|
|
10
11
|
const provider_models_1 = require("./provider-models");
|
|
@@ -170,6 +171,7 @@ function buildFailoverContext(errorMessage, classification, currentProvider, cur
|
|
|
170
171
|
errorSummary,
|
|
171
172
|
classification,
|
|
172
173
|
currentProvider,
|
|
174
|
+
currentModel,
|
|
173
175
|
currentLane,
|
|
174
176
|
agentName,
|
|
175
177
|
workingProviders,
|
|
@@ -264,3 +266,36 @@ async function runMachineProviderFailoverInventory(agentName, currentProvider, o
|
|
|
264
266
|
});
|
|
265
267
|
return inventory;
|
|
266
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Re-verify a failover candidate is actually reachable right before we mutate
|
|
271
|
+
* provider state. The inventory ping that produced the candidate may be stale
|
|
272
|
+
* (creds revoked between inventory and reply); without this preflight, an
|
|
273
|
+
* agent-driven "switch to <provider>" can move the lane onto an unreachable
|
|
274
|
+
* provider and brick the next turn.
|
|
275
|
+
*/
|
|
276
|
+
async function validateFailoverSwitchCandidate(agentName, candidate, options = {}) {
|
|
277
|
+
const ping = options.ping ?? provider_ping_1.pingProvider;
|
|
278
|
+
const refreshPool = options.refreshPool ?? provider_credentials_1.refreshProviderCredentialPool;
|
|
279
|
+
const poolResult = await refreshPool(agentName);
|
|
280
|
+
if (!poolResult.ok) {
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
classification: "auth-failure",
|
|
284
|
+
message: `provider credential pool unavailable (${poolResult.reason}): ${poolResult.error}`,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const record = poolResult.pool.providers[candidate.provider];
|
|
288
|
+
if (!record) {
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
classification: "auth-failure",
|
|
292
|
+
message: `no credentials configured for ${candidate.provider}`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const config = { ...record.credentials, ...record.config };
|
|
296
|
+
const result = await ping(candidate.provider, config, { model: candidate.model });
|
|
297
|
+
if (!result.ok) {
|
|
298
|
+
return { ok: false, classification: result.classification, message: result.message };
|
|
299
|
+
}
|
|
300
|
+
return { ok: true };
|
|
301
|
+
}
|