@ouro.bot/cli 0.1.0-alpha.467 → 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,21 @@
|
|
|
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
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.468",
|
|
14
|
+
"changes": [
|
|
15
|
+
"Published package binary smoke checks now retry transient npm registry/network failures such as `ECONNRESET`, so a successful publish is not left red by one aborted `npm exec` fetch.",
|
|
16
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the release-smoke retry hardening release."
|
|
17
|
+
]
|
|
18
|
+
},
|
|
4
19
|
{
|
|
5
20
|
"version": "0.1.0-alpha.467",
|
|
6
21
|
"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);
|
package/dist/mailroom/core.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
+
}
|