@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
|
@@ -112,6 +112,24 @@ function normalizeContent(content) {
|
|
|
112
112
|
.filter((part) => part != null && typeof part === "object")
|
|
113
113
|
.map((part) => ({ ...part }));
|
|
114
114
|
}
|
|
115
|
+
const SYNTHETIC_TIMESTAMP_PREFIX_RE = /^(?:(?:\[(?:just now|-\d+[mhd])\])\s*)+/i;
|
|
116
|
+
function stripSyntheticTimestampPrefix(text) {
|
|
117
|
+
return text.replace(SYNTHETIC_TIMESTAMP_PREFIX_RE, "");
|
|
118
|
+
}
|
|
119
|
+
function sanitizeConversationContent(role, content) {
|
|
120
|
+
if (role !== "user" && role !== "assistant")
|
|
121
|
+
return content;
|
|
122
|
+
if (typeof content === "string")
|
|
123
|
+
return stripSyntheticTimestampPrefix(content);
|
|
124
|
+
if (!Array.isArray(content))
|
|
125
|
+
return content;
|
|
126
|
+
return content.map((part) => {
|
|
127
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
128
|
+
return { ...part, text: stripSyntheticTimestampPrefix(part.text) };
|
|
129
|
+
}
|
|
130
|
+
return part;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
115
133
|
function normalizeToolCalls(rawToolCalls) {
|
|
116
134
|
if (!Array.isArray(rawToolCalls))
|
|
117
135
|
return [];
|
|
@@ -141,10 +159,11 @@ function normalizeRole(role) {
|
|
|
141
159
|
function normalizeMessage(message) {
|
|
142
160
|
const record = message;
|
|
143
161
|
const role = normalizeRole(record.role);
|
|
162
|
+
const normalizedContent = sanitizeConversationContent(role, normalizeContent(record.content));
|
|
144
163
|
if (role === "assistant") {
|
|
145
164
|
return {
|
|
146
165
|
role,
|
|
147
|
-
content:
|
|
166
|
+
content: normalizedContent,
|
|
148
167
|
name: typeof record.name === "string" ? record.name : null,
|
|
149
168
|
toolCallId: null,
|
|
150
169
|
toolCalls: normalizeToolCalls(record.tool_calls),
|
|
@@ -163,7 +182,7 @@ function normalizeMessage(message) {
|
|
|
163
182
|
}
|
|
164
183
|
return {
|
|
165
184
|
role,
|
|
166
|
-
content:
|
|
185
|
+
content: normalizedContent ?? "",
|
|
167
186
|
name: typeof record.name === "string" ? record.name : null,
|
|
168
187
|
toolCallId: null,
|
|
169
188
|
toolCalls: [],
|
|
@@ -286,7 +305,7 @@ function repairSessionMessages(messages) {
|
|
|
286
305
|
});
|
|
287
306
|
return result.map(toProviderMessage);
|
|
288
307
|
}
|
|
289
|
-
function
|
|
308
|
+
function repairToolCallSequences(messages) {
|
|
290
309
|
const normalized = messages.map(normalizeMessage);
|
|
291
310
|
const validCallIds = new Set();
|
|
292
311
|
for (const msg of normalized) {
|
|
@@ -313,8 +332,74 @@ function stripOrphanedToolResults(messages) {
|
|
|
313
332
|
meta: { removed },
|
|
314
333
|
});
|
|
315
334
|
}
|
|
335
|
+
let injected = 0;
|
|
336
|
+
for (let i = 0; i < repaired.length; i++) {
|
|
337
|
+
const msg = repaired[i];
|
|
338
|
+
if (msg.role !== "assistant" || msg.toolCalls.length === 0)
|
|
339
|
+
continue;
|
|
340
|
+
const resultIds = new Set();
|
|
341
|
+
for (let j = i + 1; j < repaired.length; j++) {
|
|
342
|
+
const following = repaired[j];
|
|
343
|
+
if (following.role === "tool" && following.toolCallId !== null) {
|
|
344
|
+
resultIds.add(following.toolCallId);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (following.role === "assistant" || following.role === "user")
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
const missing = msg.toolCalls.filter((toolCall) => !resultIds.has(toolCall.id));
|
|
351
|
+
if (missing.length === 0)
|
|
352
|
+
continue;
|
|
353
|
+
const syntheticResults = missing.map((toolCall) => ({
|
|
354
|
+
role: "tool",
|
|
355
|
+
content: "error: tool call was interrupted (previous turn timed out or was aborted)",
|
|
356
|
+
name: null,
|
|
357
|
+
toolCallId: toolCall.id,
|
|
358
|
+
toolCalls: [],
|
|
359
|
+
hadToolCallsField: false,
|
|
360
|
+
}));
|
|
361
|
+
let insertAt = i + 1;
|
|
362
|
+
while (insertAt < repaired.length && repaired[insertAt].role === "tool")
|
|
363
|
+
insertAt++;
|
|
364
|
+
repaired.splice(insertAt, 0, ...syntheticResults);
|
|
365
|
+
injected += syntheticResults.length;
|
|
366
|
+
}
|
|
367
|
+
if (injected > 0) {
|
|
368
|
+
(0, runtime_1.emitNervesEvent)({
|
|
369
|
+
level: "info",
|
|
370
|
+
event: "mind.session_orphan_tool_call_repair",
|
|
371
|
+
component: "mind",
|
|
372
|
+
message: "injected synthetic tool results for orphaned tool calls",
|
|
373
|
+
meta: { injected },
|
|
374
|
+
});
|
|
375
|
+
}
|
|
316
376
|
return repaired.map(toProviderMessage);
|
|
317
377
|
}
|
|
378
|
+
function canonicalizeSystemMessageSequence(messages) {
|
|
379
|
+
const normalized = messages.map(normalizeMessage);
|
|
380
|
+
const firstSystemIndex = normalized.findIndex((msg) => msg.role === "system");
|
|
381
|
+
if (firstSystemIndex === -1)
|
|
382
|
+
return normalized.map(toProviderMessage);
|
|
383
|
+
const extraSystemCount = normalized.filter((msg) => msg.role === "system").length - 1;
|
|
384
|
+
if (firstSystemIndex === 0 && extraSystemCount === 0) {
|
|
385
|
+
return normalized.map(toProviderMessage);
|
|
386
|
+
}
|
|
387
|
+
const primarySystem = normalized[firstSystemIndex];
|
|
388
|
+
const nonSystemMessages = normalized.filter((msg) => msg.role !== "system");
|
|
389
|
+
const repaired = [primarySystem, ...nonSystemMessages].map(toProviderMessage);
|
|
390
|
+
(0, runtime_1.emitNervesEvent)({
|
|
391
|
+
level: "info",
|
|
392
|
+
event: "mind.session_system_prompt_repair",
|
|
393
|
+
component: "mind",
|
|
394
|
+
message: "canonicalized session system prompt sequence",
|
|
395
|
+
meta: {
|
|
396
|
+
firstSystemIndex,
|
|
397
|
+
extraSystemCount,
|
|
398
|
+
finalMessageCount: repaired.length,
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
return repaired;
|
|
402
|
+
}
|
|
318
403
|
function migrateToolNames(messages) {
|
|
319
404
|
const safeMessages = messages.filter((message) => Boolean(message) && typeof message === "object");
|
|
320
405
|
let migrated = 0;
|
|
@@ -359,7 +444,7 @@ function sanitizeProviderMessages(messages) {
|
|
|
359
444
|
meta: { violations },
|
|
360
445
|
});
|
|
361
446
|
}
|
|
362
|
-
return migrateToolNames(
|
|
447
|
+
return canonicalizeSystemMessageSequence(migrateToolNames(repairToolCallSequences(repairSessionMessages(normalized.map(toProviderMessage)))));
|
|
363
448
|
}
|
|
364
449
|
function stampIngressTime(msg) {
|
|
365
450
|
msg._ingressAt = new Date().toISOString();
|
|
@@ -587,11 +672,12 @@ function parseSessionEnvelope(raw, options = {}) {
|
|
|
587
672
|
const time = event.time;
|
|
588
673
|
const relations = event.relations;
|
|
589
674
|
const provenance = event.provenance;
|
|
675
|
+
const content = sanitizeConversationContent(role, normalizeContent(event.content));
|
|
590
676
|
return {
|
|
591
677
|
id: typeof event.id === "string" ? event.id : makeEventId(index + 1),
|
|
592
678
|
sequence: typeof event.sequence === "number" ? event.sequence : index + 1,
|
|
593
679
|
role,
|
|
594
|
-
content
|
|
680
|
+
content,
|
|
595
681
|
name: typeof event.name === "string" ? event.name : null,
|
|
596
682
|
toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : null,
|
|
597
683
|
toolCalls: normalizeToolCalls(event.toolCalls),
|
|
@@ -60,6 +60,7 @@ const config_1 = require("./config");
|
|
|
60
60
|
const daemon_health_1 = require("./daemon/daemon-health");
|
|
61
61
|
const prompt_1 = require("../mind/prompt");
|
|
62
62
|
const provider_visibility_1 = require("./provider-visibility");
|
|
63
|
+
const mail_import_discovery_1 = require("./mail-import-discovery");
|
|
63
64
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
64
65
|
const DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
65
66
|
function isLiveCodingSessionStatus(status) {
|
|
@@ -271,6 +272,14 @@ async function buildTurnContext(input) {
|
|
|
271
272
|
otherCodingSessions = [];
|
|
272
273
|
/* v8 ignore stop */
|
|
273
274
|
}
|
|
275
|
+
const backgroundOperations = (0, mail_import_discovery_1.listVisibleBackgroundOperations)({
|
|
276
|
+
agentName,
|
|
277
|
+
agentRoot,
|
|
278
|
+
repoRoot: process.cwd(),
|
|
279
|
+
homeDir: process.env.HOME,
|
|
280
|
+
nowMs: Date.now(),
|
|
281
|
+
limit: 5,
|
|
282
|
+
});
|
|
274
283
|
// Inner work state
|
|
275
284
|
const innerWorkState = readInnerWorkState();
|
|
276
285
|
// Task board
|
|
@@ -333,6 +342,7 @@ async function buildTurnContext(input) {
|
|
|
333
342
|
obligationCount: pendingObligations.length,
|
|
334
343
|
bridgeCount: activeBridges.length,
|
|
335
344
|
codingSessionCount: codingSessions.length,
|
|
345
|
+
backgroundOperationCount: backgroundOperations.length,
|
|
336
346
|
episodeCount: recentEpisodes.length,
|
|
337
347
|
providerLaneCount: providerVisibility.lanes.length,
|
|
338
348
|
},
|
|
@@ -344,6 +354,7 @@ async function buildTurnContext(input) {
|
|
|
344
354
|
pendingObligations,
|
|
345
355
|
codingSessions,
|
|
346
356
|
otherCodingSessions,
|
|
357
|
+
backgroundOperations,
|
|
347
358
|
innerWorkState,
|
|
348
359
|
taskBoard,
|
|
349
360
|
returnObligations,
|
package/dist/mind/context.js
CHANGED
|
@@ -242,7 +242,7 @@ function loadSession(filePath) {
|
|
|
242
242
|
if (!envelope)
|
|
243
243
|
return null;
|
|
244
244
|
return {
|
|
245
|
-
messages: (0, session_events_1.
|
|
245
|
+
messages: (0, session_events_1.sanitizeProviderMessages)((0, session_events_1.projectProviderMessages)(envelope)),
|
|
246
246
|
events: envelope.events,
|
|
247
247
|
lastUsage: envelope.lastUsage ?? undefined,
|
|
248
248
|
state: denormalizeContinuityState(envelope.state),
|
package/dist/mind/prompt.js
CHANGED
|
@@ -467,7 +467,7 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
|
|
|
467
467
|
lines.push("mail setup AX: if a human asks me to set up email, I do not hand them a terminal checklist. I guide the flow end-to-end: name the current phase, run agent-runnable commands myself with shell/tools when available, ask the human only for human-required facts or browser actions, wait for their reply, verify the result, then continue.");
|
|
468
468
|
lines.push("mail setup hard rule: never tell the human to run `ouro account ensure`, `ouro connect mail`, `ouro mail import-mbox`, `ouro status`, or `ouro doctor` for setup. Say what I am about to run, run it myself, and report the result. If my current surface cannot run shell/tools, I ask for a tool-capable Ouro setup session or companion to continue; I do not offload CLI operation to the human.");
|
|
469
469
|
lines.push("mail setup truth: Agent Mail uses Mailroom, not HEY OAuth/IMAP. For the full work substrate account, the agent-runnable command is `ouro account ensure --agent <agent> --owner-email <email> --source hey`; use `ouro connect mail --agent <agent> --owner-email <email> --source hey` for mail-only repair/provisioning, or `--no-delegated-source` for native-only mail. The detailed runbook is `docs/agent-mail-setup.md`.");
|
|
470
|
-
lines.push("mail setup truth: HEY archive bootstrap
|
|
470
|
+
lines.push("mail setup truth: HEY archive bootstrap still depends on HEY's browser-only export, but I should not offload path-discovery ceremony to the human. If browser MCP/Playwright downloaded the archive, I first try `ouro mail import-mbox --discover --owner-email <email> --source hey --agent <agent>` so Ouro can find the sandboxed copy in a repo/worktree `.playwright-mcp`, the home `.playwright-mcp`, or Downloads. If discovery cannot find a unique file, then I ask the human for the local MBOX path and run `ouro mail import-mbox --file <path> --owner-email <email> --source hey --agent <agent>` myself.");
|
|
471
471
|
lines.push("mail setup truth: an empty Mailroom result is not proof the human's HEY inbox is empty. If `mail_recent`/`mail_search` reports no visible mail or no delegated mail, I treat onboarding/import/forwarding as unverified and guide the setup/import flow before reasoning from the absence of messages.");
|
|
472
472
|
lines.push("mail validation answer shape: when a human asks for Agent Mail golden paths, answer with only these four named paths before claiming setup works:");
|
|
473
473
|
lines.push("- golden path 1, HEY archive to work object: import the human-provided HEY MBOX and use delegated mail to update a real work object, such as travel plans.");
|
|
@@ -475,7 +475,7 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
|
|
|
475
475
|
lines.push(channel === "mcp"
|
|
476
476
|
? "- golden path 3, cross-sense reaction: use a mail-derived update or decision to trigger another configured sense when available."
|
|
477
477
|
: "- golden path 3, cross-sense reaction: use a mail-derived update or decision to trigger another configured sense, such as texting the family member on iMessage when BlueBubbles is available.");
|
|
478
|
-
lines.push("- golden path 4, Ouro
|
|
478
|
+
lines.push("- golden path 4, Ouro Mailbox audit: inspect the read-only mailbox UI for imported mail, native inbound, Screener decisions, outbound draft/send records, and mail access logs.");
|
|
479
479
|
lines.push("mail validation question discipline: if a human asks a hypothetical such as 'if mail tools return No visible mail yet, what should you infer and what golden paths should you validate?', answer that hypothetical first. Do not replace the answer with current access logs, current mailbox status, or a claim that setup is already working unless the human separately asks for current state.");
|
|
480
480
|
lines.push("mail validation diagnostics: health checks, bounded mail tools, access logs, and UI inspection can support validation, but they are evidence inside those paths, not additional paths. If asked to name golden paths, do not include diagnostic commands, tool names, or status checks in the answer.");
|
|
481
481
|
lines.push("mail diagnostic naming: `ouro doctor` is installation-wide; do not invent `ouro doctor --agent <agent>`.");
|
|
@@ -700,7 +700,10 @@ function pendingMessagesSection(options) {
|
|
|
700
700
|
const pending = options?.pendingMessages;
|
|
701
701
|
if (!pending || pending.length === 0)
|
|
702
702
|
return "";
|
|
703
|
-
const lines = [
|
|
703
|
+
const lines = [
|
|
704
|
+
"## pending messages",
|
|
705
|
+
"these are fresh work items that arrived for me this turn. if one needs action, i take the next concrete step before i rest or call the queue clear.",
|
|
706
|
+
];
|
|
704
707
|
for (const msg of pending) {
|
|
705
708
|
lines.push(`- from ${msg.from}: ${msg.content}`);
|
|
706
709
|
}
|
|
@@ -66,6 +66,7 @@ const DISPATCH_EXEMPT_PATTERNS = [
|
|
|
66
66
|
"daemon/vault-items",
|
|
67
67
|
// Shared utility modules: pure helpers consumed by modules that own observability.
|
|
68
68
|
"arc/json-store",
|
|
69
|
+
"heart/mail-import-discovery",
|
|
69
70
|
"repertoire/api-client",
|
|
70
71
|
"repertoire/github-client",
|
|
71
72
|
"mind/embedding-provider",
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Nerves observation layer — shared typed readers for runtime state.
|
|
4
4
|
*
|
|
5
5
|
* This module re-exports domain types that multiple consumers need
|
|
6
|
-
* (
|
|
6
|
+
* (Mailbox, CLI, agent tools) so they don't maintain parallel type universes.
|
|
7
7
|
*
|
|
8
|
-
* The
|
|
8
|
+
* The Mailbox UI consumes these through the HTTP API, but the same
|
|
9
9
|
* observation functions back CLI commands and future native clients.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|