@ouro.bot/cli 0.1.0-alpha.454 → 0.1.0-alpha.456
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/daemon/cli-exec.js +161 -66
- package/dist/heart/daemon/cli-help.js +12 -0
- package/dist/heart/daemon/cli-parse.js +13 -0
- package/dist/heart/daemon/runtime-logging.js +1 -1
- package/dist/heart/daemon/sense-manager.js +10 -2
- package/dist/heart/outlook/readers/mail.js +72 -2
- package/dist/mailroom/attention.js +154 -0
- package/dist/mailroom/blob-store.js +150 -2
- package/dist/mailroom/core.js +160 -9
- package/dist/mailroom/file-store.js +164 -2
- package/dist/mailroom/outbound.js +177 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +16 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/prompt.js +1 -1
- package/dist/outlook-ui/assets/index-BBM5EysT.js +61 -0
- package/dist/outlook-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/outlook-ui/index.html +2 -2
- package/dist/repertoire/guardrails.js +25 -0
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-mail.js +480 -4
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +224 -0
- package/package.json +1 -1
- package/dist/outlook-ui/assets/index-BXw3xmUo.js +0 -61
- package/dist/outlook-ui/assets/index-D4Wg-o8Z.css +0 -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.456",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Ouro Outlook session transcripts now respect the reader's scroll position during live refresh: if a human has scrolled up to inspect older iMessage/Teams/CLI history, background transcript updates no longer yank the pane back to the bottom.",
|
|
8
|
+
"Open transcripts no longer insert a transient loading row over already-rendered messages during refresh, removing the visible stutter that made long iMessage transcripts feel unstable.",
|
|
9
|
+
"Inner-dialog transcript panes now use the same bottom-stickiness behavior, so loading earlier messages or jumping to landmarks does not immediately undo the user's navigation."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.455",
|
|
14
|
+
"changes": [
|
|
15
|
+
"Agent Mail now has a complete all-agent onboarding runbook around `ouro account ensure --agent <agent>`, including native `@ouro.bot` mailbox setup, optional delegated human-mail aliases, vault-coupled private keys, bundle-local encrypted Mailroom state, HEY MBOX import, live forwarding boundaries, and golden-path verification.",
|
|
16
|
+
"Screener decisions now persist family-authorized sender policies for discard and quarantine as well as allow decisions, so future mail from a discarded sender goes directly to the retained recovery drawer instead of repeatedly interrupting the agent.",
|
|
17
|
+
"The system prompt now points agents at `docs/agent-mail-setup.md` and distinguishes full work-substrate account setup from mail-only repair/provisioning, while docs contract coverage locks the native-vs-delegated trust model, recovery semantics, human-only DNS/HEY/MX gates, confirmed outbound sends, and Ouro Outlook audit expectations."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.454",
|
|
6
22
|
"changes": [
|
|
@@ -326,6 +326,7 @@ function agentResolutionFailureMode(command) {
|
|
|
326
326
|
case "vault.config.set":
|
|
327
327
|
case "vault.config.status":
|
|
328
328
|
case "connect":
|
|
329
|
+
case "account.ensure":
|
|
329
330
|
case "mail.import-mbox":
|
|
330
331
|
case "auth.run":
|
|
331
332
|
case "auth.verify":
|
|
@@ -2681,11 +2682,93 @@ async function executeConnectBlueBubbles(agent, deps) {
|
|
|
2681
2682
|
],
|
|
2682
2683
|
});
|
|
2683
2684
|
}
|
|
2684
|
-
|
|
2685
|
+
function isPlainRecord(value) {
|
|
2686
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2687
|
+
}
|
|
2688
|
+
function readMailroomRegistryFromDisk(registryPath) {
|
|
2689
|
+
if (!fs.existsSync(registryPath))
|
|
2690
|
+
return undefined;
|
|
2691
|
+
const parsed = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
|
2692
|
+
if (!isPlainRecord(parsed) || parsed.schemaVersion !== 1 || !Array.isArray(parsed.mailboxes) || !Array.isArray(parsed.sourceGrants)) {
|
|
2693
|
+
throw new Error(`mailroom registry at ${registryPath} is not a valid Mailroom registry`);
|
|
2694
|
+
}
|
|
2695
|
+
return parsed;
|
|
2696
|
+
}
|
|
2697
|
+
function mailroomPrivateKeys(mailroom) {
|
|
2698
|
+
const keys = isPlainRecord(mailroom?.privateKeys) ? mailroom.privateKeys : {};
|
|
2699
|
+
return Object.fromEntries(Object.entries(keys).filter((entry) => typeof entry[1] === "string"));
|
|
2700
|
+
}
|
|
2701
|
+
async function promptDelegatedMailSource(deps) {
|
|
2702
|
+
const promptInput = requirePromptInput(deps, "Agent Mail setup");
|
|
2703
|
+
const ownerEmail = (await promptInput("Delegated owner email for first source alias [blank to skip]: ")).trim();
|
|
2704
|
+
const source = ownerEmail
|
|
2705
|
+
? ((await promptInput("Delegated source label [hey]: ")).trim() || "hey")
|
|
2706
|
+
: "";
|
|
2707
|
+
return { ownerEmail, source };
|
|
2708
|
+
}
|
|
2709
|
+
async function ensureAgentMailroom(agent, input, deps, progressLabel) {
|
|
2685
2710
|
if (agent === "SerpentGuide") {
|
|
2686
2711
|
throw new Error("SerpentGuide has no persistent runtime credentials. Connect mail on the hatchling agent instead.");
|
|
2687
2712
|
}
|
|
2688
|
-
const
|
|
2713
|
+
const agentRoot = providerCliAgentRoot({ agent }, deps);
|
|
2714
|
+
const mailStateDir = path.join(agentRoot, "state", "mailroom");
|
|
2715
|
+
const progress = createHumanCommandProgress(deps, progressLabel);
|
|
2716
|
+
let stored;
|
|
2717
|
+
let mailboxAddress = `${agent.toLowerCase()}@ouro.bot`;
|
|
2718
|
+
let sourceAlias = null;
|
|
2719
|
+
let registryPath = path.join(mailStateDir, "registry.json");
|
|
2720
|
+
let storePath = mailStateDir;
|
|
2721
|
+
try {
|
|
2722
|
+
progress.startPhase("checking Mailroom runtime config");
|
|
2723
|
+
const current = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
2724
|
+
if (!current.ok && current.reason !== "missing") {
|
|
2725
|
+
throw new Error(`cannot read existing runtime credentials from ${current.itemPath}: ${current.error}`);
|
|
2726
|
+
}
|
|
2727
|
+
const currentConfig = current.ok ? current.config : {};
|
|
2728
|
+
const existingMailroom = isPlainRecord(currentConfig.mailroom) ? currentConfig.mailroom : undefined;
|
|
2729
|
+
registryPath = stringField(existingMailroom ?? {}, "registryPath") || registryPath;
|
|
2730
|
+
storePath = stringField(existingMailroom ?? {}, "storePath") || storePath;
|
|
2731
|
+
progress.updateDetail("ensuring Mailroom identity");
|
|
2732
|
+
const ensured = (0, core_1.ensureMailboxRegistry)({
|
|
2733
|
+
agentId: agent,
|
|
2734
|
+
registry: readMailroomRegistryFromDisk(registryPath),
|
|
2735
|
+
keys: mailroomPrivateKeys(existingMailroom),
|
|
2736
|
+
ownerEmail: input.ownerEmail || undefined,
|
|
2737
|
+
source: input.source || undefined,
|
|
2738
|
+
});
|
|
2739
|
+
mailboxAddress = ensured.mailboxAddress;
|
|
2740
|
+
sourceAlias = ensured.sourceAlias;
|
|
2741
|
+
fs.mkdirSync(path.dirname(registryPath), { recursive: true });
|
|
2742
|
+
fs.mkdirSync(storePath, { recursive: true });
|
|
2743
|
+
fs.writeFileSync(registryPath, `${JSON.stringify(ensured.registry, null, 2)}\n`, "utf-8");
|
|
2744
|
+
const nextConfig = {
|
|
2745
|
+
...currentConfig,
|
|
2746
|
+
mailroom: {
|
|
2747
|
+
...(existingMailroom ?? {}),
|
|
2748
|
+
mailboxAddress,
|
|
2749
|
+
registryPath,
|
|
2750
|
+
storePath,
|
|
2751
|
+
privateKeys: ensured.keys,
|
|
2752
|
+
},
|
|
2753
|
+
};
|
|
2754
|
+
progress.updateDetail("storing private mail keys in vault");
|
|
2755
|
+
stored = await (0, runtime_credentials_1.upsertRuntimeCredentialConfig)(agent, nextConfig, providerCliNow(deps));
|
|
2756
|
+
progress.updateDetail("enabling Mail sense in agent.json");
|
|
2757
|
+
enableAgentSense(agent, "mail", deps);
|
|
2758
|
+
progress.completePhase("checking Mailroom runtime config", "mail ready");
|
|
2759
|
+
progress.end();
|
|
2760
|
+
}
|
|
2761
|
+
catch (error) {
|
|
2762
|
+
progress.end();
|
|
2763
|
+
throw error;
|
|
2764
|
+
}
|
|
2765
|
+
/* v8 ignore next -- defensive: successful setup always stores before leaving the guarded block. @preserve */
|
|
2766
|
+
if (!stored)
|
|
2767
|
+
throw new Error("Mailroom setup did not store runtime credentials");
|
|
2768
|
+
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
2769
|
+
return { mailboxAddress, sourceAlias, registryPath, storePath, stored, syncSummary };
|
|
2770
|
+
}
|
|
2771
|
+
async function executeConnectMail(agent, deps) {
|
|
2689
2772
|
writeConnectorIntro(deps, {
|
|
2690
2773
|
title: "Connect Agent Mail",
|
|
2691
2774
|
subtitle: `${agent} gets a private Mailroom identity and delegated source lane.`,
|
|
@@ -2720,87 +2803,96 @@ async function executeConnectMail(agent, deps) {
|
|
|
2720
2803
|
"The registry and encrypted local store live under the agent bundle state.",
|
|
2721
2804
|
],
|
|
2722
2805
|
});
|
|
2723
|
-
const
|
|
2724
|
-
const
|
|
2725
|
-
? ((await promptInput("Delegated source label [hey]: ")).trim() || "hey")
|
|
2726
|
-
: "";
|
|
2727
|
-
const agentRoot = providerCliAgentRoot({ agent }, deps);
|
|
2728
|
-
const mailStateDir = path.join(agentRoot, "state", "mailroom");
|
|
2729
|
-
const registryPath = path.join(mailStateDir, "registry.json");
|
|
2730
|
-
const storePath = mailStateDir;
|
|
2731
|
-
const progress = createHumanCommandProgress(deps, "connect mail");
|
|
2732
|
-
let stored;
|
|
2733
|
-
let mailboxAddress = `${agent.toLowerCase()}@ouro.bot`;
|
|
2734
|
-
let sourceAlias = null;
|
|
2735
|
-
try {
|
|
2736
|
-
progress.startPhase("provisioning Mailroom identity");
|
|
2737
|
-
const provisioned = (0, core_1.provisionMailboxRegistry)({
|
|
2738
|
-
agentId: agent,
|
|
2739
|
-
ownerEmail: ownerEmail || undefined,
|
|
2740
|
-
source: source || undefined,
|
|
2741
|
-
});
|
|
2742
|
-
mailboxAddress = provisioned.registry.mailboxes[0].canonicalAddress;
|
|
2743
|
-
sourceAlias = provisioned.registry.sourceGrants[0]?.aliasAddress ?? null;
|
|
2744
|
-
fs.mkdirSync(mailStateDir, { recursive: true });
|
|
2745
|
-
fs.writeFileSync(registryPath, `${JSON.stringify(provisioned.registry, null, 2)}\n`, "utf-8");
|
|
2746
|
-
progress.updateDetail("checking existing runtime config");
|
|
2747
|
-
const current = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
2748
|
-
if (!current.ok && current.reason !== "missing") {
|
|
2749
|
-
progress.end();
|
|
2750
|
-
throw new Error(`cannot read existing runtime credentials from ${current.itemPath}: ${current.error}`);
|
|
2751
|
-
}
|
|
2752
|
-
const nextConfig = {
|
|
2753
|
-
...(current.ok ? current.config : {}),
|
|
2754
|
-
mailroom: {
|
|
2755
|
-
mailboxAddress,
|
|
2756
|
-
registryPath,
|
|
2757
|
-
storePath,
|
|
2758
|
-
privateKeys: provisioned.keys,
|
|
2759
|
-
},
|
|
2760
|
-
};
|
|
2761
|
-
progress.updateDetail("storing private mail keys in vault");
|
|
2762
|
-
stored = await (0, runtime_credentials_1.upsertRuntimeCredentialConfig)(agent, nextConfig, providerCliNow(deps));
|
|
2763
|
-
progress.updateDetail("enabling Mail sense in agent.json");
|
|
2764
|
-
enableAgentSense(agent, "mail", deps);
|
|
2765
|
-
progress.completePhase("provisioning Mailroom identity", "mail ready");
|
|
2766
|
-
progress.end();
|
|
2767
|
-
}
|
|
2768
|
-
catch (error) {
|
|
2769
|
-
progress.end();
|
|
2770
|
-
throw error;
|
|
2771
|
-
}
|
|
2772
|
-
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
2806
|
+
const setup = await promptDelegatedMailSource(deps);
|
|
2807
|
+
const outcome = await ensureAgentMailroom(agent, setup, deps, "connect mail");
|
|
2773
2808
|
return writeCapabilityOutcome(deps, {
|
|
2774
2809
|
subtitle: `${agent}'s Mail sense is configured.`,
|
|
2775
2810
|
summary: `Agent Mail is ready for ${agent}.`,
|
|
2776
2811
|
whatChanged: [
|
|
2777
|
-
`Mailbox: ${mailboxAddress}`,
|
|
2778
|
-
...(sourceAlias ? [`Delegated alias: ${sourceAlias}`] : []),
|
|
2779
|
-
`Registry: ${registryPath}`,
|
|
2780
|
-
`Encrypted store: ${storePath}`,
|
|
2781
|
-
`Stored: ${stored.itemPath}`,
|
|
2812
|
+
`Mailbox: ${outcome.mailboxAddress}`,
|
|
2813
|
+
...(outcome.sourceAlias ? [`Delegated alias: ${outcome.sourceAlias}`] : []),
|
|
2814
|
+
`Registry: ${outcome.registryPath}`,
|
|
2815
|
+
`Encrypted store: ${outcome.storePath}`,
|
|
2816
|
+
`Stored: ${outcome.stored.itemPath}`,
|
|
2782
2817
|
"agent.json: senses.mail.enabled = true",
|
|
2783
2818
|
"private mail keys were not printed",
|
|
2784
|
-
...(syncSummary ? [syncSummary] : []),
|
|
2819
|
+
...(outcome.syncSummary ? [outcome.syncSummary] : []),
|
|
2785
2820
|
],
|
|
2786
2821
|
nextMoves: [
|
|
2787
2822
|
"Run ouro up so the daemon picks up the Mail sense.",
|
|
2788
|
-
sourceAlias
|
|
2789
|
-
? `Use ${sourceAlias} for HEY forwarding or Extensions after the SMTP ingress proof is accepted.`
|
|
2823
|
+
outcome.sourceAlias
|
|
2824
|
+
? `Use ${outcome.sourceAlias} for HEY forwarding or Extensions after the SMTP ingress proof is accepted.`
|
|
2790
2825
|
: "Add delegated source aliases when you are ready to mirror human mail.",
|
|
2791
2826
|
],
|
|
2792
2827
|
fallbackLines: [
|
|
2793
2828
|
`Agent Mail connected for ${agent}`,
|
|
2794
|
-
`mailbox: ${mailboxAddress}`,
|
|
2795
|
-
...(sourceAlias ? [`delegated alias: ${sourceAlias}`] : []),
|
|
2796
|
-
`registry: ${registryPath}`,
|
|
2797
|
-
`encrypted store: ${storePath}`,
|
|
2798
|
-
`stored: ${stored.itemPath}`,
|
|
2829
|
+
`mailbox: ${outcome.mailboxAddress}`,
|
|
2830
|
+
...(outcome.sourceAlias ? [`delegated alias: ${outcome.sourceAlias}`] : []),
|
|
2831
|
+
`registry: ${outcome.registryPath}`,
|
|
2832
|
+
`encrypted store: ${outcome.storePath}`,
|
|
2833
|
+
`stored: ${outcome.stored.itemPath}`,
|
|
2799
2834
|
"agent.json: senses.mail.enabled = true",
|
|
2800
2835
|
"private mail keys were not printed",
|
|
2801
2836
|
"",
|
|
2802
2837
|
"Next: run `ouro up` so the daemon picks up the Mail sense.",
|
|
2803
|
-
...(syncSummary ? [syncSummary] : []),
|
|
2838
|
+
...(outcome.syncSummary ? [outcome.syncSummary] : []),
|
|
2839
|
+
],
|
|
2840
|
+
});
|
|
2841
|
+
}
|
|
2842
|
+
async function executeAccountEnsure(command, deps) {
|
|
2843
|
+
writeConnectorIntro(deps, {
|
|
2844
|
+
title: "Ensure Agent Account",
|
|
2845
|
+
subtitle: `${command.agent}'s Ouro work substrate gets its vault-backed coordinates.`,
|
|
2846
|
+
summary: "Prepare the portable runtime account shape that couples vault, Mailroom, and enabled senses.",
|
|
2847
|
+
sections: [
|
|
2848
|
+
{
|
|
2849
|
+
title: "Account substrate",
|
|
2850
|
+
lines: [
|
|
2851
|
+
"runtime/config remains the private vault item for portable integration secrets.",
|
|
2852
|
+
"Mailroom gets a private native mailbox and optional delegated source alias.",
|
|
2853
|
+
"agent.json records that the Mail sense is enabled.",
|
|
2854
|
+
],
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
title: "Human-only edges",
|
|
2858
|
+
lines: [
|
|
2859
|
+
"This command does not perform browser auth, HEY forwarding, DNS edits, MX cutover, or autonomous sending enablement.",
|
|
2860
|
+
],
|
|
2861
|
+
},
|
|
2862
|
+
],
|
|
2863
|
+
fallbackLines: [
|
|
2864
|
+
`Ensure Ouro work substrate for ${command.agent}`,
|
|
2865
|
+
"This creates or preserves runtime/config Mailroom coordinates and enables the Mail sense.",
|
|
2866
|
+
],
|
|
2867
|
+
});
|
|
2868
|
+
const setup = await promptDelegatedMailSource(deps);
|
|
2869
|
+
const outcome = await ensureAgentMailroom(command.agent, setup, deps, "account ensure");
|
|
2870
|
+
return writeCapabilityOutcome(deps, {
|
|
2871
|
+
subtitle: `${command.agent}'s work substrate account is configured.`,
|
|
2872
|
+
summary: `Ouro work substrate is ready for ${command.agent}.`,
|
|
2873
|
+
whatChanged: [
|
|
2874
|
+
"Vault item: runtime/config",
|
|
2875
|
+
`Mailbox: ${outcome.mailboxAddress}`,
|
|
2876
|
+
...(outcome.sourceAlias ? [`Delegated alias: ${outcome.sourceAlias}`] : []),
|
|
2877
|
+
`Registry: ${outcome.registryPath}`,
|
|
2878
|
+
`Encrypted store: ${outcome.storePath}`,
|
|
2879
|
+
"agent.json: senses.mail.enabled = true",
|
|
2880
|
+
...(outcome.syncSummary ? [outcome.syncSummary] : []),
|
|
2881
|
+
],
|
|
2882
|
+
nextMoves: [
|
|
2883
|
+
"Run ouro up so the daemon picks up the Mail sense.",
|
|
2884
|
+
"Import HEY MBOX mail only after the human has exported the file.",
|
|
2885
|
+
"Keep production MX and autonomous send disabled until the final explicit confirmation.",
|
|
2886
|
+
],
|
|
2887
|
+
fallbackLines: [
|
|
2888
|
+
`Ouro work substrate ready for ${command.agent}`,
|
|
2889
|
+
"vault: runtime/config",
|
|
2890
|
+
`mailbox: ${outcome.mailboxAddress}`,
|
|
2891
|
+
...(outcome.sourceAlias ? [`delegated alias: ${outcome.sourceAlias}`] : []),
|
|
2892
|
+
`registry: ${outcome.registryPath}`,
|
|
2893
|
+
`encrypted store: ${outcome.storePath}`,
|
|
2894
|
+
"agent.json: senses.mail.enabled = true",
|
|
2895
|
+
...(outcome.syncSummary ? [outcome.syncSummary] : []),
|
|
2804
2896
|
],
|
|
2805
2897
|
});
|
|
2806
2898
|
}
|
|
@@ -4274,6 +4366,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
4274
4366
|
if (command.kind === "connect") {
|
|
4275
4367
|
return executeConnect(command, deps);
|
|
4276
4368
|
}
|
|
4369
|
+
if (command.kind === "account.ensure") {
|
|
4370
|
+
return executeAccountEnsure(command, deps);
|
|
4371
|
+
}
|
|
4277
4372
|
if (command.kind === "mail.import-mbox") {
|
|
4278
4373
|
return executeMailImportMbox(command, deps);
|
|
4279
4374
|
}
|
|
@@ -163,6 +163,13 @@ exports.COMMAND_REGISTRY = {
|
|
|
163
163
|
example: "ouro auth",
|
|
164
164
|
subcommands: ["verify", "switch"],
|
|
165
165
|
},
|
|
166
|
+
account: {
|
|
167
|
+
category: "Auth",
|
|
168
|
+
description: "Ensure the agent's vault-backed work substrate account, including Mailroom setup",
|
|
169
|
+
usage: "ouro account ensure [--agent <name>]",
|
|
170
|
+
example: "ouro account ensure --agent slugger",
|
|
171
|
+
subcommands: ["ensure"],
|
|
172
|
+
},
|
|
166
173
|
connect: {
|
|
167
174
|
category: "Auth",
|
|
168
175
|
description: "Set up providers, portable integrations, and local senses from one guided screen",
|
|
@@ -309,6 +316,11 @@ const SUBCOMMAND_HELP = {
|
|
|
309
316
|
usage: "ouro connect mail [--agent <name>]",
|
|
310
317
|
example: "ouro connect mail",
|
|
311
318
|
},
|
|
319
|
+
"account ensure": {
|
|
320
|
+
description: "Idempotently prepare an agent's vault-backed work substrate account and private Mailroom mailbox",
|
|
321
|
+
usage: "ouro account ensure [--agent <name>]",
|
|
322
|
+
example: "ouro account ensure --agent slugger",
|
|
323
|
+
},
|
|
312
324
|
"mail import-mbox": {
|
|
313
325
|
description: "Import a HEY or other MBOX export into an existing delegated Mailroom source grant",
|
|
314
326
|
usage: "ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]",
|
|
@@ -83,6 +83,7 @@ function usage() {
|
|
|
83
83
|
" ouro config model [--agent <name>] <model-name>",
|
|
84
84
|
" ouro config models [--agent <name>]",
|
|
85
85
|
" ouro auth [--agent <name>] [--provider <provider>]",
|
|
86
|
+
" ouro account ensure [--agent <name>]",
|
|
86
87
|
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]",
|
|
87
88
|
" ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]",
|
|
88
89
|
" ouro auth verify [--agent <name>] [--provider <provider>]",
|
|
@@ -648,6 +649,16 @@ function parseMailCommand(args) {
|
|
|
648
649
|
...(source ? { source } : {}),
|
|
649
650
|
};
|
|
650
651
|
}
|
|
652
|
+
function parseAccountCommand(args) {
|
|
653
|
+
const [sub, ...subArgs] = args;
|
|
654
|
+
if (sub !== "ensure") {
|
|
655
|
+
throw new Error("Usage: ouro account ensure [--agent <name>]");
|
|
656
|
+
}
|
|
657
|
+
const { agent, rest } = extractAgentFlag(subArgs);
|
|
658
|
+
if (rest.length > 0)
|
|
659
|
+
throw new Error("Usage: ouro account ensure [--agent <name>]");
|
|
660
|
+
return { kind: "account.ensure", ...(agent ? { agent } : {}) };
|
|
661
|
+
}
|
|
651
662
|
function parseProviderUseCommand(args) {
|
|
652
663
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
653
664
|
const { facing, rest: afterFacing } = extractFacingFlag(afterAgent);
|
|
@@ -1130,6 +1141,8 @@ function parseOuroCommand(args) {
|
|
|
1130
1141
|
return parseHatchCommand(args.slice(1));
|
|
1131
1142
|
if (head === "auth")
|
|
1132
1143
|
return parseAuthCommand(args.slice(1));
|
|
1144
|
+
if (head === "account")
|
|
1145
|
+
return parseAccountCommand(args.slice(1));
|
|
1133
1146
|
if (head === "connect")
|
|
1134
1147
|
return parseConnectCommand(args.slice(1));
|
|
1135
1148
|
if (head === "vault")
|
|
@@ -215,6 +215,13 @@ function runtimeInfoFor(status) {
|
|
|
215
215
|
return { runtime: "running" };
|
|
216
216
|
return { runtime: "error" };
|
|
217
217
|
}
|
|
218
|
+
function managedSenseEntry(sense) {
|
|
219
|
+
if (sense === "teams")
|
|
220
|
+
return "senses/teams-entry.js";
|
|
221
|
+
if (sense === "bluebubbles")
|
|
222
|
+
return "senses/bluebubbles/entry.js";
|
|
223
|
+
return "senses/mail-entry.js";
|
|
224
|
+
}
|
|
218
225
|
function blueBubblesRuntimeStateIsFresh(lastCheckedAt, now = Date.now()) {
|
|
219
226
|
if (!lastCheckedAt) {
|
|
220
227
|
return false;
|
|
@@ -281,12 +288,12 @@ class DaemonSenseManager {
|
|
|
281
288
|
return [agent, { senses, facts }];
|
|
282
289
|
}));
|
|
283
290
|
const managedSenseAgents = [...this.contexts.entries()].flatMap(([agent, context]) => {
|
|
284
|
-
return ["teams", "bluebubbles"]
|
|
291
|
+
return ["teams", "bluebubbles", "mail"]
|
|
285
292
|
.filter((sense) => context.senses[sense].enabled)
|
|
286
293
|
.map((sense) => ({
|
|
287
294
|
name: `${agent}:${sense}`,
|
|
288
295
|
agentArg: agent,
|
|
289
|
-
entry: sense
|
|
296
|
+
entry: managedSenseEntry(sense),
|
|
290
297
|
channel: sense,
|
|
291
298
|
autoStart: true,
|
|
292
299
|
}));
|
|
@@ -371,6 +378,7 @@ class DaemonSenseManager {
|
|
|
371
378
|
},
|
|
372
379
|
mail: {
|
|
373
380
|
configured: context.facts.mail.configured,
|
|
381
|
+
...(runtime.get(agent)?.mail ?? {}),
|
|
374
382
|
},
|
|
375
383
|
};
|
|
376
384
|
const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
|
|
@@ -12,10 +12,17 @@ function emptyFolders() {
|
|
|
12
12
|
return [
|
|
13
13
|
{ id: "imbox", label: "Imbox", count: 0 },
|
|
14
14
|
{ id: "screener", label: "Screener", count: 0 },
|
|
15
|
+
{ id: "discarded", label: "Discarded", count: 0 },
|
|
16
|
+
{ id: "quarantine", label: "Quarantine", count: 0 },
|
|
17
|
+
{ id: "draft", label: "Drafts", count: 0 },
|
|
18
|
+
{ id: "sent", label: "Sent", count: 0 },
|
|
15
19
|
{ id: "delegated", label: "Delegated", count: 0 },
|
|
16
20
|
{ id: "native", label: "Native", count: 0 },
|
|
17
21
|
];
|
|
18
22
|
}
|
|
23
|
+
function emptyRecovery() {
|
|
24
|
+
return { discardedCount: 0, quarantineCount: 0 };
|
|
25
|
+
}
|
|
19
26
|
function unavailableMailView(agentName, status, error) {
|
|
20
27
|
return {
|
|
21
28
|
status,
|
|
@@ -25,6 +32,9 @@ function unavailableMailView(agentName, status, error) {
|
|
|
25
32
|
store: null,
|
|
26
33
|
folders: emptyFolders(),
|
|
27
34
|
messages: [],
|
|
35
|
+
screener: [],
|
|
36
|
+
outbound: [],
|
|
37
|
+
recovery: emptyRecovery(),
|
|
28
38
|
accessLog: [],
|
|
29
39
|
error,
|
|
30
40
|
};
|
|
@@ -41,6 +51,16 @@ function unavailableMessageView(agentName, status, error) {
|
|
|
41
51
|
};
|
|
42
52
|
}
|
|
43
53
|
function mailSummary(message) {
|
|
54
|
+
const provenance = {
|
|
55
|
+
placement: message.placement,
|
|
56
|
+
compartmentKind: message.compartmentKind,
|
|
57
|
+
ownerEmail: message.ownerEmail ?? null,
|
|
58
|
+
source: message.source ?? null,
|
|
59
|
+
recipient: message.recipient,
|
|
60
|
+
mailboxId: message.mailboxId,
|
|
61
|
+
grantId: message.grantId ?? null,
|
|
62
|
+
trustReason: message.trustReason,
|
|
63
|
+
};
|
|
44
64
|
return {
|
|
45
65
|
id: message.id,
|
|
46
66
|
subject: message.private.subject,
|
|
@@ -57,12 +77,17 @@ function mailSummary(message) {
|
|
|
57
77
|
recipient: message.recipient,
|
|
58
78
|
attachmentCount: message.private.attachments.length,
|
|
59
79
|
untrustedContentWarning: message.private.untrustedContentWarning,
|
|
80
|
+
provenance,
|
|
60
81
|
};
|
|
61
82
|
}
|
|
62
|
-
function buildFolders(messages) {
|
|
83
|
+
function buildFolders(messages, outbound) {
|
|
63
84
|
const folders = [
|
|
64
85
|
{ id: "imbox", label: "Imbox", count: messages.filter((message) => message.placement === "imbox").length },
|
|
65
86
|
{ id: "screener", label: "Screener", count: messages.filter((message) => message.placement === "screener").length },
|
|
87
|
+
{ id: "discarded", label: "Discarded", count: messages.filter((message) => message.placement === "discarded").length },
|
|
88
|
+
{ id: "quarantine", label: "Quarantine", count: messages.filter((message) => message.placement === "quarantine").length },
|
|
89
|
+
{ id: "draft", label: "Drafts", count: outbound.filter((record) => record.status === "draft").length },
|
|
90
|
+
{ id: "sent", label: "Sent", count: outbound.filter((record) => record.status === "sent").length },
|
|
66
91
|
{ id: "delegated", label: "Delegated", count: messages.filter((message) => message.compartmentKind === "delegated").length },
|
|
67
92
|
{ id: "native", label: "Native", count: messages.filter((message) => message.compartmentKind === "native").length },
|
|
68
93
|
];
|
|
@@ -77,6 +102,45 @@ function buildFolders(messages) {
|
|
|
77
102
|
}
|
|
78
103
|
return folders;
|
|
79
104
|
}
|
|
105
|
+
function screenerCandidate(candidate) {
|
|
106
|
+
return {
|
|
107
|
+
id: candidate.id,
|
|
108
|
+
messageId: candidate.messageId,
|
|
109
|
+
senderEmail: candidate.senderEmail,
|
|
110
|
+
senderDisplay: candidate.senderDisplay,
|
|
111
|
+
recipient: candidate.recipient,
|
|
112
|
+
source: candidate.source ?? null,
|
|
113
|
+
ownerEmail: candidate.ownerEmail ?? null,
|
|
114
|
+
status: candidate.status,
|
|
115
|
+
placement: candidate.placement,
|
|
116
|
+
trustReason: candidate.trustReason,
|
|
117
|
+
firstSeenAt: candidate.firstSeenAt,
|
|
118
|
+
lastSeenAt: candidate.lastSeenAt,
|
|
119
|
+
messageCount: candidate.messageCount,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function outboundRecord(record) {
|
|
123
|
+
return {
|
|
124
|
+
id: record.id,
|
|
125
|
+
status: record.status,
|
|
126
|
+
from: record.from,
|
|
127
|
+
to: record.to,
|
|
128
|
+
cc: record.cc,
|
|
129
|
+
bcc: record.bcc,
|
|
130
|
+
subject: record.subject,
|
|
131
|
+
createdAt: record.createdAt,
|
|
132
|
+
updatedAt: record.updatedAt,
|
|
133
|
+
sentAt: record.sentAt ?? null,
|
|
134
|
+
transport: record.transport ?? null,
|
|
135
|
+
reason: record.reason,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function buildRecovery(messages) {
|
|
139
|
+
return {
|
|
140
|
+
discardedCount: messages.filter((message) => message.placement === "discarded").length,
|
|
141
|
+
quarantineCount: messages.filter((message) => message.placement === "quarantine").length,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
80
144
|
function accessEntries(entries) {
|
|
81
145
|
return entries
|
|
82
146
|
.slice()
|
|
@@ -113,6 +177,9 @@ async function readMailView(agentName) {
|
|
|
113
177
|
const stored = await resolved.store.listMessages({ agentId: agentName, limit: OUTLOOK_MAIL_COUNT_LIMIT });
|
|
114
178
|
const decrypted = (0, file_store_1.decryptMessages)(stored, resolved.config.privateKeys);
|
|
115
179
|
const summaries = decrypted.map(mailSummary);
|
|
180
|
+
const screener = (await resolved.store.listScreenerCandidates({ agentId: agentName, status: "pending", limit: 100 }))
|
|
181
|
+
.map(screenerCandidate);
|
|
182
|
+
const outbound = (await resolved.store.listMailOutbound(agentName)).map(outboundRecord);
|
|
116
183
|
await resolved.store.recordAccess({
|
|
117
184
|
agentId: agentName,
|
|
118
185
|
tool: "outlook_mail_list",
|
|
@@ -129,8 +196,11 @@ async function readMailView(agentName) {
|
|
|
129
196
|
kind: resolved.storeKind,
|
|
130
197
|
label: resolved.storeLabel,
|
|
131
198
|
},
|
|
132
|
-
folders: buildFolders(summaries),
|
|
199
|
+
folders: buildFolders(summaries, outbound),
|
|
133
200
|
messages: summaries.slice(0, OUTLOOK_MAIL_LIST_LIMIT),
|
|
201
|
+
screener,
|
|
202
|
+
outbound,
|
|
203
|
+
recovery: buildRecovery(summaries),
|
|
134
204
|
accessLog,
|
|
135
205
|
error: null,
|
|
136
206
|
};
|