@ouro.bot/cli 0.1.0-alpha.468 → 0.1.0-alpha.469

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 CHANGED
@@ -1,6 +1,14 @@
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.469",
6
+ "changes": [
7
+ "Delegated HEY MBOX imports now preserve archive freshness/provenance, use message dates when present, suppress historical Screener wakeups, and show `source fresh through` in `ouro mail import-mbox` output.",
8
+ "Agent Mail setup docs and source-state helpers now orient Slugger to drive browser automation while the human handles HEY login/MFA/export/forwarding confirmation, including recovery for wrong forwarding targets such as `slugger@ouro.bot`.",
9
+ "Delegated source setup state now emits body-safe Nerves events, and coverage locks the source-state, MBOX import, CLI freshness fallback, and Mailroom ingest provenance branches."
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "0.1.0-alpha.468",
6
14
  "changes": [
@@ -3391,6 +3391,8 @@ async function executeMailImportMbox(command, deps) {
3391
3391
  `scanned: ${result.scanned}`,
3392
3392
  `imported: ${result.imported}`,
3393
3393
  `duplicates: ${result.duplicates}`,
3394
+ `source fresh through: ${result.sourceFreshThrough ?? "unknown"}`,
3395
+ "archive imports are historical; they do not create Screener wakeups.",
3394
3396
  "body reads remain explicit through mail_recent/mail_search/mail_thread and are access-logged.",
3395
3397
  ].join("\n");
3396
3398
  deps.writeStdout(message);
@@ -305,6 +305,9 @@ function candidateSender(input) {
305
305
  return { email: "(unknown)", display: input.envelope.mailFrom.trim() };
306
306
  }
307
307
  }
308
+ function normalizedIngestProvenance(input) {
309
+ return input ?? { schemaVersion: 1, kind: "smtp" };
310
+ }
308
311
  async function buildStoredMailMessage(input) {
309
312
  const parsed = await (0, mailparser_1.simpleParser)(input.rawMime);
310
313
  const id = messageStorageId(input.envelope, input.rawMime);
@@ -355,10 +358,12 @@ async function buildStoredMailMessage(input) {
355
358
  rawSha256,
356
359
  rawSize: input.rawMime.byteLength,
357
360
  privateEnvelope: privatePayload,
361
+ ingest: normalizedIngestProvenance(input.ingest),
358
362
  receivedAt,
359
363
  };
360
364
  const sender = candidateSender({ parsedFrom: privateEnvelope.from, envelope: input.envelope });
361
- const candidate = input.classification?.candidate || placement === "screener"
365
+ const shouldCreateCandidate = input.classification?.candidate ?? placement === "screener";
366
+ const candidate = shouldCreateCandidate
362
367
  ? {
363
368
  schemaVersion: 1,
364
369
  id: `candidate_${id}`,
@@ -368,6 +368,7 @@ async function ingestRawMailToStore(input) {
368
368
  envelope: input.envelope,
369
369
  rawMime: input.rawMime,
370
370
  receivedAt: input.receivedAt,
371
+ ...(input.ingest ? { ingest: input.ingest } : {}),
371
372
  classification,
372
373
  });
373
374
  accepted.push(result.message);
@@ -48,13 +48,32 @@ function findSourceGrant(input) {
48
48
  }
49
49
  return grants[0];
50
50
  }
51
- async function envelopeForMboxMessage(rawMessage, grant) {
51
+ async function parseMboxMessage(rawMessage, grant) {
52
52
  const parsed = await (0, mailparser_1.simpleParser)(rawMessage);
53
53
  const mailFrom = parsed.from?.value?.[0]?.address;
54
54
  return {
55
- mailFrom: mailFrom ? (0, core_1.normalizeMailAddress)(mailFrom) : "",
56
- rcptTo: [(0, core_1.normalizeMailAddress)(grant.aliasAddress)],
57
- remoteAddress: "mbox-import",
55
+ rawMessage,
56
+ envelope: {
57
+ mailFrom: mailFrom ? (0, core_1.normalizeMailAddress)(mailFrom) : "",
58
+ rcptTo: [(0, core_1.normalizeMailAddress)(grant.aliasAddress)],
59
+ remoteAddress: "mbox-import",
60
+ },
61
+ ...(parsed.date ? { messageDate: parsed.date } : {}),
62
+ };
63
+ }
64
+ function latestMessageDate(messages) {
65
+ const timestamps = messages
66
+ .map((message) => message.messageDate?.getTime())
67
+ .filter((timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp));
68
+ if (timestamps.length === 0)
69
+ return null;
70
+ return new Date(Math.max(...timestamps)).toISOString();
71
+ }
72
+ function historicalImportClassification(resolvedPlacement, sourceGrant) {
73
+ return {
74
+ placement: resolvedPlacement,
75
+ candidate: false,
76
+ trustReason: `delegated source grant ${sourceGrant.source} historical mbox import`,
58
77
  };
59
78
  }
60
79
  async function importMboxToStore(input) {
@@ -75,12 +94,23 @@ async function importMboxToStore(input) {
75
94
  let duplicates = 0;
76
95
  const messages = [];
77
96
  const rawMessages = splitMboxMessages(input.rawMbox);
78
- for (const rawMessage of rawMessages) {
97
+ const parsedMessages = await Promise.all(rawMessages.map((rawMessage) => parseMboxMessage(rawMessage, sourceGrant)));
98
+ const importedAt = (input.importedAt ?? new Date()).toISOString();
99
+ const sourceFreshThrough = latestMessageDate(parsedMessages);
100
+ for (const parsedMessage of parsedMessages) {
79
101
  const result = await input.store.putRawMessage({
80
102
  resolved,
81
- envelope: await envelopeForMboxMessage(rawMessage, sourceGrant),
82
- rawMime: rawMessage,
83
- receivedAt: input.importedAt,
103
+ envelope: parsedMessage.envelope,
104
+ rawMime: parsedMessage.rawMessage,
105
+ receivedAt: parsedMessage.messageDate ?? input.importedAt,
106
+ ingest: {
107
+ schemaVersion: 1,
108
+ kind: "mbox-import",
109
+ importedAt,
110
+ sourceFreshThrough,
111
+ attentionSuppressed: true,
112
+ },
113
+ classification: historicalImportClassification(resolved.defaultPlacement, sourceGrant),
84
114
  });
85
115
  messages.push(result.message);
86
116
  if (result.created)
@@ -100,6 +130,7 @@ async function importMboxToStore(input) {
100
130
  scanned: rawMessages.length,
101
131
  imported,
102
132
  duplicates,
133
+ sourceFreshThrough,
103
134
  messages,
104
135
  };
105
136
  }
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDelegatedMailSourceState = createDelegatedMailSourceState;
4
+ exports.markMboxBackfillComplete = markMboxBackfillComplete;
5
+ exports.markForwardingProbeResult = markForwardingProbeResult;
6
+ exports.renderDelegatedMailSourceNextStep = renderDelegatedMailSourceNextStep;
7
+ const runtime_1 = require("../nerves/runtime");
8
+ const core_1 = require("./core");
9
+ const HUMAN_REQUIRED = [
10
+ "browser_auth",
11
+ "mfa_or_captcha",
12
+ "export_download",
13
+ "forwarding_confirmation",
14
+ ];
15
+ function normalizedSource(source) {
16
+ const value = source.trim().toLowerCase();
17
+ return value || "hey";
18
+ }
19
+ function normalizedAgentId(agentId) {
20
+ return agentId.trim().toLowerCase();
21
+ }
22
+ function createDelegatedMailSourceState(input) {
23
+ const aliasAddress = (0, core_1.normalizeMailAddress)(input.aliasAddress);
24
+ const state = {
25
+ schemaVersion: 1,
26
+ agentId: normalizedAgentId(input.agentId),
27
+ ownerEmail: (0, core_1.normalizeMailAddress)(input.ownerEmail),
28
+ source: normalizedSource(input.source),
29
+ aliasAddress,
30
+ backfill: {
31
+ status: "not_started",
32
+ },
33
+ forwarding: {
34
+ status: "blocked_by_human",
35
+ targetAlias: aliasAddress,
36
+ browserAutomationOwner: "agent",
37
+ humanRequired: [...HUMAN_REQUIRED],
38
+ },
39
+ };
40
+ (0, runtime_1.emitNervesEvent)({
41
+ component: "senses",
42
+ event: "senses.mail_delegated_source_state_created",
43
+ message: "delegated mail source setup state created",
44
+ meta: {
45
+ agentId: state.agentId,
46
+ source: state.source,
47
+ forwardingStatus: state.forwarding.status,
48
+ backfillStatus: state.backfill.status,
49
+ },
50
+ });
51
+ return state;
52
+ }
53
+ function markMboxBackfillComplete(state, input) {
54
+ const nextState = {
55
+ ...state,
56
+ backfill: {
57
+ status: "ready",
58
+ scanned: input.scanned,
59
+ imported: input.imported,
60
+ duplicates: input.duplicates,
61
+ sourceFreshThrough: input.sourceFreshThrough,
62
+ completedAt: input.completedAt,
63
+ },
64
+ };
65
+ (0, runtime_1.emitNervesEvent)({
66
+ component: "senses",
67
+ event: "senses.mail_delegated_source_backfill_ready",
68
+ message: "delegated mail source archive backfill marked ready",
69
+ meta: {
70
+ agentId: nextState.agentId,
71
+ source: nextState.source,
72
+ scanned: input.scanned,
73
+ imported: input.imported,
74
+ duplicates: input.duplicates,
75
+ sourceFreshThroughKnown: input.sourceFreshThrough !== null,
76
+ },
77
+ });
78
+ return nextState;
79
+ }
80
+ function markForwardingProbeResult(state, input) {
81
+ const expectedRecipient = (0, core_1.normalizeMailAddress)(state.forwarding.targetAlias);
82
+ if (!input.observedRecipient) {
83
+ const nextState = {
84
+ ...state,
85
+ forwarding: {
86
+ ...state.forwarding,
87
+ status: "pending_propagation",
88
+ observedRecipient: null,
89
+ expectedRecipient,
90
+ recoveryAction: "Wait briefly, then have Slugger re-check the delegated source alias before asking the human to change HEY again.",
91
+ },
92
+ };
93
+ (0, runtime_1.emitNervesEvent)({
94
+ component: "senses",
95
+ event: "senses.mail_delegated_source_forwarding_probe",
96
+ message: "delegated mail source forwarding probe checked",
97
+ meta: {
98
+ agentId: nextState.agentId,
99
+ source: nextState.source,
100
+ status: nextState.forwarding.status,
101
+ observedRecipientPresent: false,
102
+ expectedRecipientDomain: expectedRecipient.split("@")[1],
103
+ messageIdPresent: Boolean(input.messageId),
104
+ },
105
+ });
106
+ return nextState;
107
+ }
108
+ const observedRecipient = (0, core_1.normalizeMailAddress)(input.observedRecipient);
109
+ if (observedRecipient !== expectedRecipient) {
110
+ const nextState = {
111
+ ...state,
112
+ forwarding: {
113
+ ...state.forwarding,
114
+ status: "failed_recoverable",
115
+ observedRecipient,
116
+ expectedRecipient,
117
+ ...(input.messageId ? { lastProbeMessageId: input.messageId } : {}),
118
+ recoveryAction: `HEY is forwarding to ${observedRecipient}. Slugger must correct the HEY forwarding target to ${expectedRecipient}; do not import or label that probe as delegated Ari HEY mail.`,
119
+ },
120
+ };
121
+ (0, runtime_1.emitNervesEvent)({
122
+ component: "senses",
123
+ event: "senses.mail_delegated_source_forwarding_probe",
124
+ message: "delegated mail source forwarding probe checked",
125
+ meta: {
126
+ agentId: nextState.agentId,
127
+ source: nextState.source,
128
+ status: nextState.forwarding.status,
129
+ observedRecipientPresent: true,
130
+ observedRecipientMatches: false,
131
+ expectedRecipientDomain: expectedRecipient.split("@")[1],
132
+ observedRecipientDomain: observedRecipient.split("@")[1],
133
+ messageIdPresent: Boolean(input.messageId),
134
+ },
135
+ });
136
+ return nextState;
137
+ }
138
+ const nextState = {
139
+ ...state,
140
+ forwarding: {
141
+ ...state.forwarding,
142
+ status: "ready",
143
+ observedRecipient,
144
+ expectedRecipient,
145
+ verifiedAt: input.checkedAt,
146
+ ...(input.messageId ? { lastProbeMessageId: input.messageId } : {}),
147
+ },
148
+ };
149
+ (0, runtime_1.emitNervesEvent)({
150
+ component: "senses",
151
+ event: "senses.mail_delegated_source_forwarding_probe",
152
+ message: "delegated mail source forwarding probe checked",
153
+ meta: {
154
+ agentId: nextState.agentId,
155
+ source: nextState.source,
156
+ status: nextState.forwarding.status,
157
+ observedRecipientPresent: true,
158
+ observedRecipientMatches: true,
159
+ expectedRecipientDomain: expectedRecipient.split("@")[1],
160
+ observedRecipientDomain: observedRecipient.split("@")[1],
161
+ messageIdPresent: Boolean(input.messageId),
162
+ },
163
+ });
164
+ return nextState;
165
+ }
166
+ function renderDelegatedMailSourceNextStep(state) {
167
+ if (state.forwarding.status === "ready") {
168
+ return `${state.source} forwarding is verified for ${state.aliasAddress}.`;
169
+ }
170
+ return [
171
+ `Slugger should continue ${state.source.toUpperCase()} setup with browser automation where it is safe.`,
172
+ "The human remains at the keyboard for login, MFA/CAPTCHA, export download, and final forwarding confirmation.",
173
+ `Forward ${state.ownerEmail}'s ${state.source} mailbox to ${state.aliasAddress}.`,
174
+ "Do not use the native agent mailbox as the forwarding target.",
175
+ ].join(" ");
176
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.468",
3
+ "version": "0.1.0-alpha.469",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",