@ouro.bot/cli 0.1.0-alpha.450 → 0.1.0-alpha.451
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 +8 -0
- package/dist/heart/daemon/cli-exec.js +222 -1
- package/dist/heart/daemon/cli-help.js +19 -2
- package/dist/heart/daemon/cli-parse.js +44 -3
- package/dist/heart/daemon/doctor.js +1 -1
- package/dist/heart/daemon/sense-manager.js +29 -2
- package/dist/heart/identity.js +4 -1
- package/dist/heart/outlook/outlook-http-hooks.js +2 -0
- package/dist/heart/outlook/outlook-http-routes.js +14 -2
- package/dist/heart/outlook/outlook-read.js +4 -1
- package/dist/heart/outlook/readers/mail.js +203 -0
- package/dist/heart/sense-truth.js +8 -4
- package/dist/heart/turn-context.js +14 -4
- package/dist/mailroom/blob-store.js +154 -0
- package/dist/mailroom/core.js +387 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +230 -0
- package/dist/mailroom/mbox-import.js +105 -0
- package/dist/mailroom/reader.js +150 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mind/friends/channel.js +9 -0
- package/dist/mind/friends/types.js +1 -1
- package/dist/mind/prompt.js +16 -4
- package/dist/outlook-ui/assets/index-BXw3xmUo.js +61 -0
- package/dist/outlook-ui/assets/index-D4Wg-o8Z.css +1 -0
- package/dist/outlook-ui/index.html +2 -2
- package/dist/repertoire/tools-mail.js +209 -0
- package/dist/senses/commands.js +1 -1
- package/package.json +6 -1
- package/dist/outlook-ui/assets/index-BAcU08c-.css +0 -1
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +0 -61
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.451",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Agent Mail is now a first-class sense: agent identity, prompt truth, status/doctor surfaces, friend channel capabilities, and the base tool registry all understand `mail` alongside CLI, Teams, and BlueBubbles.",
|
|
8
|
+
"`ouro connect mail --agent <agent>` provisions a vault-coupled Mailroom identity with private mail keys stored in the agent vault, a non-secret bundle registry, a canonical `@ouro.bot` mailbox, and delegated source aliases such as HEY shadow imbox addresses.",
|
|
9
|
+
"The Mailroom substrate can accept SMTP, screen unknown recipients, import MBOX exports, store encrypted message/raw payloads in file or Azure Blob backends, and expose bounded read/audit tools for agents to read mail as mail."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
4
12
|
{
|
|
5
13
|
"version": "0.1.0-alpha.450",
|
|
6
14
|
"changes": [
|
|
@@ -79,6 +79,9 @@ const provider_models_1 = require("../provider-models");
|
|
|
79
79
|
const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
|
|
80
80
|
const update_checker_1 = require("../versioning/update-checker");
|
|
81
81
|
const sync_1 = require("../sync");
|
|
82
|
+
const core_1 = require("../../mailroom/core");
|
|
83
|
+
const file_store_1 = require("../../mailroom/file-store");
|
|
84
|
+
const mbox_import_1 = require("../../mailroom/mbox-import");
|
|
82
85
|
const cli_parse_1 = require("./cli-parse");
|
|
83
86
|
const cli_parse_2 = require("./cli-parse");
|
|
84
87
|
const cli_help_1 = require("./cli-help");
|
|
@@ -323,6 +326,7 @@ function agentResolutionFailureMode(command) {
|
|
|
323
326
|
case "vault.config.set":
|
|
324
327
|
case "vault.config.status":
|
|
325
328
|
case "connect":
|
|
329
|
+
case "mail.import-mbox":
|
|
326
330
|
case "auth.run":
|
|
327
331
|
case "auth.verify":
|
|
328
332
|
case "auth.switch":
|
|
@@ -2030,6 +2034,8 @@ function enableAgentSense(agent, sense, deps) {
|
|
|
2030
2034
|
...senses,
|
|
2031
2035
|
cli: senses.cli ?? { enabled: true },
|
|
2032
2036
|
teams: senses.teams ?? { enabled: false },
|
|
2037
|
+
bluebubbles: senses.bluebubbles ?? { enabled: false },
|
|
2038
|
+
mail: senses.mail ?? { enabled: false },
|
|
2033
2039
|
[sense]: { ...existing, enabled: true },
|
|
2034
2040
|
};
|
|
2035
2041
|
fs.writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
@@ -2044,6 +2050,7 @@ function readConnectBaySenseFlags(agent, deps) {
|
|
|
2044
2050
|
return {
|
|
2045
2051
|
teamsEnabled: parsed.senses?.teams?.enabled === true,
|
|
2046
2052
|
blueBubblesEnabled: parsed.senses?.bluebubbles?.enabled === true,
|
|
2053
|
+
mailEnabled: parsed.senses?.mail?.enabled === true,
|
|
2047
2054
|
};
|
|
2048
2055
|
}
|
|
2049
2056
|
async function buildConnectMenu(agent, deps, onProgress) {
|
|
@@ -2073,7 +2080,7 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2073
2080
|
const runtimeConfig = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
2074
2081
|
onProgress?.("loading this machine's settings");
|
|
2075
2082
|
const machineRuntime = await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(agent, currentMachineId(deps), { preserveCachedOnFailure: true });
|
|
2076
|
-
const { teamsEnabled, blueBubblesEnabled } = readConnectBaySenseFlags(agent, deps);
|
|
2083
|
+
const { teamsEnabled, blueBubblesEnabled, mailEnabled } = readConnectBaySenseFlags(agent, deps);
|
|
2077
2084
|
const perplexityApiKey = runtimeConfig.ok
|
|
2078
2085
|
? readRuntimeConfigString(runtimeConfig.config, "integrations.perplexityApiKey")
|
|
2079
2086
|
: null;
|
|
@@ -2150,6 +2157,18 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2150
2157
|
? "attached"
|
|
2151
2158
|
: "not attached"
|
|
2152
2159
|
: machineRuntimeReadStatus(machineRuntime);
|
|
2160
|
+
const mailroomConfig = runtimeConfig.ok && runtimeConfig.config.mailroom && typeof runtimeConfig.config.mailroom === "object" && !Array.isArray(runtimeConfig.config.mailroom)
|
|
2161
|
+
? runtimeConfig.config.mailroom
|
|
2162
|
+
: {};
|
|
2163
|
+
const mailPrivateKeys = mailroomConfig.privateKeys;
|
|
2164
|
+
const hasMailPrivateKeys = !!mailPrivateKeys && typeof mailPrivateKeys === "object" && !Array.isArray(mailPrivateKeys) && Object.values(mailPrivateKeys).some((value) => typeof value === "string" && value.trim().length > 0);
|
|
2165
|
+
const mailStatus = runtimeConfig.ok
|
|
2166
|
+
? hasRuntimeConfigValue(runtimeConfig.config, "mailroom.mailboxAddress")
|
|
2167
|
+
&& hasMailPrivateKeys
|
|
2168
|
+
&& mailEnabled
|
|
2169
|
+
? "ready"
|
|
2170
|
+
: "missing"
|
|
2171
|
+
: runtimeConfigReadStatus(runtimeConfig);
|
|
2153
2172
|
const entries = [
|
|
2154
2173
|
{
|
|
2155
2174
|
option: "1",
|
|
@@ -2215,6 +2234,19 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2215
2234
|
status: blueBubblesStatus,
|
|
2216
2235
|
}) ? `ouro connect bluebubbles --agent ${agent}` : undefined,
|
|
2217
2236
|
},
|
|
2237
|
+
{
|
|
2238
|
+
option: "6",
|
|
2239
|
+
name: "Agent Mail",
|
|
2240
|
+
section: "Portable",
|
|
2241
|
+
status: mailStatus,
|
|
2242
|
+
description: "Vault-coupled Mailroom mailbox and delegated source aliases.",
|
|
2243
|
+
nextAction: (0, connect_bay_1.connectEntryNeedsAttention)({
|
|
2244
|
+
option: "6",
|
|
2245
|
+
name: "Agent Mail",
|
|
2246
|
+
section: "Portable",
|
|
2247
|
+
status: mailStatus,
|
|
2248
|
+
}) ? `ouro connect mail --agent ${agent}` : undefined,
|
|
2249
|
+
},
|
|
2218
2250
|
];
|
|
2219
2251
|
const isTTY = connectMenuIsTTY(deps);
|
|
2220
2252
|
return (0, connect_bay_1.renderConnectBay)(entries, {
|
|
@@ -2649,6 +2681,185 @@ async function executeConnectBlueBubbles(agent, deps) {
|
|
|
2649
2681
|
],
|
|
2650
2682
|
});
|
|
2651
2683
|
}
|
|
2684
|
+
async function executeConnectMail(agent, deps) {
|
|
2685
|
+
if (agent === "SerpentGuide") {
|
|
2686
|
+
throw new Error("SerpentGuide has no persistent runtime credentials. Connect mail on the hatchling agent instead.");
|
|
2687
|
+
}
|
|
2688
|
+
const promptInput = requirePromptInput(deps, "Agent Mail setup");
|
|
2689
|
+
writeConnectorIntro(deps, {
|
|
2690
|
+
title: "Connect Agent Mail",
|
|
2691
|
+
subtitle: `${agent} gets a private Mailroom identity and delegated source lane.`,
|
|
2692
|
+
summary: "Provision a vault-coupled mailbox, source alias, encrypted local store, and Mail sense.",
|
|
2693
|
+
sections: [
|
|
2694
|
+
{
|
|
2695
|
+
title: "Unlocks",
|
|
2696
|
+
lines: [
|
|
2697
|
+
"Private agent mail at the agent's canonical @ouro.bot address.",
|
|
2698
|
+
"HEY/source aliases that stay labeled as delegated human mail.",
|
|
2699
|
+
"Bounded read tools with access logging.",
|
|
2700
|
+
],
|
|
2701
|
+
},
|
|
2702
|
+
{
|
|
2703
|
+
title: "What you need",
|
|
2704
|
+
lines: [
|
|
2705
|
+
"Optional owner email for the first delegated source alias.",
|
|
2706
|
+
"No mail password is printed or pasted; Ouro generates mail keys and stores private keys in the agent vault.",
|
|
2707
|
+
],
|
|
2708
|
+
},
|
|
2709
|
+
{
|
|
2710
|
+
title: "Where it lives",
|
|
2711
|
+
lines: [
|
|
2712
|
+
`${agent}'s vault runtime/config item for private keys and Mailroom coordinates.`,
|
|
2713
|
+
`${agent}'s bundle state for non-secret registry and local encrypted mail cache.`,
|
|
2714
|
+
],
|
|
2715
|
+
},
|
|
2716
|
+
],
|
|
2717
|
+
fallbackLines: [
|
|
2718
|
+
`Connect Agent Mail for ${agent}`,
|
|
2719
|
+
"Ouro generates mail keys and stores private keys in the agent vault.",
|
|
2720
|
+
"The registry and encrypted local store live under the agent bundle state.",
|
|
2721
|
+
],
|
|
2722
|
+
});
|
|
2723
|
+
const ownerEmail = (await promptInput("Delegated owner email for first source alias [blank to skip]: ")).trim();
|
|
2724
|
+
const source = ownerEmail
|
|
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);
|
|
2773
|
+
return writeCapabilityOutcome(deps, {
|
|
2774
|
+
subtitle: `${agent}'s Mail sense is configured.`,
|
|
2775
|
+
summary: `Agent Mail is ready for ${agent}.`,
|
|
2776
|
+
whatChanged: [
|
|
2777
|
+
`Mailbox: ${mailboxAddress}`,
|
|
2778
|
+
...(sourceAlias ? [`Delegated alias: ${sourceAlias}`] : []),
|
|
2779
|
+
`Registry: ${registryPath}`,
|
|
2780
|
+
`Encrypted store: ${storePath}`,
|
|
2781
|
+
`Stored: ${stored.itemPath}`,
|
|
2782
|
+
"agent.json: senses.mail.enabled = true",
|
|
2783
|
+
"private mail keys were not printed",
|
|
2784
|
+
...(syncSummary ? [syncSummary] : []),
|
|
2785
|
+
],
|
|
2786
|
+
nextMoves: [
|
|
2787
|
+
"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.`
|
|
2790
|
+
: "Add delegated source aliases when you are ready to mirror human mail.",
|
|
2791
|
+
],
|
|
2792
|
+
fallbackLines: [
|
|
2793
|
+
`Agent Mail connected for ${agent}`,
|
|
2794
|
+
`mailbox: ${mailboxAddress}`,
|
|
2795
|
+
...(sourceAlias ? [`delegated alias: ${sourceAlias}`] : []),
|
|
2796
|
+
`registry: ${registryPath}`,
|
|
2797
|
+
`encrypted store: ${storePath}`,
|
|
2798
|
+
`stored: ${stored.itemPath}`,
|
|
2799
|
+
"agent.json: senses.mail.enabled = true",
|
|
2800
|
+
"private mail keys were not printed",
|
|
2801
|
+
"",
|
|
2802
|
+
"Next: run `ouro up` so the daemon picks up the Mail sense.",
|
|
2803
|
+
...(syncSummary ? [syncSummary] : []),
|
|
2804
|
+
],
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
function stringField(record, key) {
|
|
2808
|
+
const value = record[key];
|
|
2809
|
+
return typeof value === "string" ? value.trim() : "";
|
|
2810
|
+
}
|
|
2811
|
+
async function executeMailImportMbox(command, deps) {
|
|
2812
|
+
const progress = createHumanCommandProgress(deps, "mail import");
|
|
2813
|
+
try {
|
|
2814
|
+
progress.startPhase("reading Mailroom config");
|
|
2815
|
+
const runtime = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true });
|
|
2816
|
+
if (!runtime.ok) {
|
|
2817
|
+
progress.end();
|
|
2818
|
+
throw new Error(`cannot read Mailroom config from ${runtime.itemPath}: ${runtime.error}`);
|
|
2819
|
+
}
|
|
2820
|
+
const mailroom = runtime.config.mailroom;
|
|
2821
|
+
if (!mailroom || typeof mailroom !== "object" || Array.isArray(mailroom)) {
|
|
2822
|
+
progress.end();
|
|
2823
|
+
throw new Error(`missing mailroom config for ${command.agent}; run 'ouro connect mail --agent ${command.agent}' first`);
|
|
2824
|
+
}
|
|
2825
|
+
const registryPath = stringField(mailroom, "registryPath");
|
|
2826
|
+
const storePath = stringField(mailroom, "storePath");
|
|
2827
|
+
if (!registryPath || !storePath) {
|
|
2828
|
+
progress.end();
|
|
2829
|
+
throw new Error(`mailroom config for ${command.agent} is missing registryPath/storePath; run 'ouro connect mail --agent ${command.agent}' again`);
|
|
2830
|
+
}
|
|
2831
|
+
progress.updateDetail("reading registry and MBOX");
|
|
2832
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
|
2833
|
+
const filePath = path.resolve(command.filePath);
|
|
2834
|
+
const rawMbox = fs.readFileSync(filePath);
|
|
2835
|
+
progress.updateDetail("importing delegated mail");
|
|
2836
|
+
const result = await (0, mbox_import_1.importMboxToStore)({
|
|
2837
|
+
registry,
|
|
2838
|
+
store: new file_store_1.FileMailroomStore({ rootDir: storePath }),
|
|
2839
|
+
agentId: command.agent,
|
|
2840
|
+
rawMbox,
|
|
2841
|
+
ownerEmail: command.ownerEmail,
|
|
2842
|
+
source: command.source,
|
|
2843
|
+
});
|
|
2844
|
+
progress.completePhase("reading Mailroom config", "imported");
|
|
2845
|
+
progress.end();
|
|
2846
|
+
const message = [
|
|
2847
|
+
`Imported MBOX for ${command.agent}`,
|
|
2848
|
+
`file: ${filePath}`,
|
|
2849
|
+
`grant: ${result.sourceGrant.grantId} (${result.sourceGrant.source}; ${result.sourceGrant.ownerEmail})`,
|
|
2850
|
+
`scanned: ${result.scanned}`,
|
|
2851
|
+
`imported: ${result.imported}`,
|
|
2852
|
+
`duplicates: ${result.duplicates}`,
|
|
2853
|
+
"body reads remain explicit through mail_recent/mail_search/mail_thread and are access-logged.",
|
|
2854
|
+
].join("\n");
|
|
2855
|
+
deps.writeStdout(message);
|
|
2856
|
+
return message;
|
|
2857
|
+
}
|
|
2858
|
+
catch (error) {
|
|
2859
|
+
progress.end();
|
|
2860
|
+
throw error;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2652
2863
|
async function executeConnectProviders(agent, deps) {
|
|
2653
2864
|
const promptInput = deps.promptInput;
|
|
2654
2865
|
if (!promptInput) {
|
|
@@ -2690,6 +2901,8 @@ function connectMenuTarget(answer) {
|
|
|
2690
2901
|
return "teams";
|
|
2691
2902
|
if (normalized === "5" || normalized === "bluebubbles" || normalized === "imessage" || normalized === "messages")
|
|
2692
2903
|
return "bluebubbles";
|
|
2904
|
+
if (normalized === "6" || normalized === "mail" || normalized === "email" || normalized === "mailroom")
|
|
2905
|
+
return "mail";
|
|
2693
2906
|
return "cancel";
|
|
2694
2907
|
}
|
|
2695
2908
|
async function executeConnect(command, deps) {
|
|
@@ -2703,6 +2916,8 @@ async function executeConnect(command, deps) {
|
|
|
2703
2916
|
return executeConnectTeams(command.agent, deps);
|
|
2704
2917
|
if (command.target === "bluebubbles")
|
|
2705
2918
|
return executeConnectBlueBubbles(command.agent, deps);
|
|
2919
|
+
if (command.target === "mail")
|
|
2920
|
+
return executeConnectMail(command.agent, deps);
|
|
2706
2921
|
const progress = createHumanCommandProgress(deps, "connect");
|
|
2707
2922
|
let menu;
|
|
2708
2923
|
try {
|
|
@@ -2721,6 +2936,7 @@ async function executeConnect(command, deps) {
|
|
|
2721
2936
|
`Run: ouro connect embeddings --agent ${command.agent}`,
|
|
2722
2937
|
`Run: ouro connect teams --agent ${command.agent}`,
|
|
2723
2938
|
`Run: ouro connect bluebubbles --agent ${command.agent}`,
|
|
2939
|
+
`Run: ouro connect mail --agent ${command.agent}`,
|
|
2724
2940
|
].join("\n");
|
|
2725
2941
|
deps.writeStdout(message);
|
|
2726
2942
|
return message;
|
|
@@ -2736,6 +2952,8 @@ async function executeConnect(command, deps) {
|
|
|
2736
2952
|
return executeConnectTeams(command.agent, deps);
|
|
2737
2953
|
if (answer === "bluebubbles")
|
|
2738
2954
|
return executeConnectBlueBubbles(command.agent, deps);
|
|
2955
|
+
if (answer === "mail")
|
|
2956
|
+
return executeConnectMail(command.agent, deps);
|
|
2739
2957
|
const message = "connect cancelled.";
|
|
2740
2958
|
deps.writeStdout(message);
|
|
2741
2959
|
return message;
|
|
@@ -4056,6 +4274,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
4056
4274
|
if (command.kind === "connect") {
|
|
4057
4275
|
return executeConnect(command, deps);
|
|
4058
4276
|
}
|
|
4277
|
+
if (command.kind === "mail.import-mbox") {
|
|
4278
|
+
return executeMailImportMbox(command, deps);
|
|
4279
|
+
}
|
|
4059
4280
|
if (command.kind === "daemon.up") {
|
|
4060
4281
|
// ── dev mode cleanup: delete dev-config.json so the wrapper stops dispatching to dev repo ──
|
|
4061
4282
|
/* v8 ignore start -- dev-config cleanup: requires real filesystem state @preserve */
|
|
@@ -166,9 +166,16 @@ exports.COMMAND_REGISTRY = {
|
|
|
166
166
|
connect: {
|
|
167
167
|
category: "Auth",
|
|
168
168
|
description: "Set up providers, portable integrations, and local senses from one guided screen",
|
|
169
|
-
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles] [--agent <name>]",
|
|
169
|
+
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]",
|
|
170
170
|
example: "ouro connect",
|
|
171
|
-
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles"],
|
|
171
|
+
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles", "mail"],
|
|
172
|
+
},
|
|
173
|
+
mail: {
|
|
174
|
+
category: "Auth",
|
|
175
|
+
description: "Import delegated mail into the agent Mailroom substrate",
|
|
176
|
+
usage: "ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]",
|
|
177
|
+
example: "ouro mail import-mbox --file ~/Downloads/hey.mbox --owner-email ari@mendelow.me --source hey --agent slugger",
|
|
178
|
+
subcommands: ["import-mbox"],
|
|
172
179
|
},
|
|
173
180
|
use: {
|
|
174
181
|
category: "Auth",
|
|
@@ -297,6 +304,16 @@ const SUBCOMMAND_HELP = {
|
|
|
297
304
|
usage: "ouro connect bluebubbles [--agent <name>]",
|
|
298
305
|
example: "ouro connect bluebubbles",
|
|
299
306
|
},
|
|
307
|
+
"connect mail": {
|
|
308
|
+
description: "Provision portable Agent Mail / Mailroom access and enable the Mail sense",
|
|
309
|
+
usage: "ouro connect mail [--agent <name>]",
|
|
310
|
+
example: "ouro connect mail",
|
|
311
|
+
},
|
|
312
|
+
"mail import-mbox": {
|
|
313
|
+
description: "Import a HEY or other MBOX export into an existing delegated Mailroom source grant",
|
|
314
|
+
usage: "ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]",
|
|
315
|
+
example: "ouro mail import-mbox --file ~/Downloads/hey.mbox --owner-email ari@mendelow.me --source hey --agent slugger",
|
|
316
|
+
},
|
|
300
317
|
"provider refresh": {
|
|
301
318
|
description: "Reload this agent's provider credentials from its vault into daemon memory",
|
|
302
319
|
usage: "ouro provider refresh [--agent <name>]",
|
|
@@ -83,7 +83,8 @@ 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 connect [providers|perplexity|embeddings|teams|bluebubbles] [--agent <name>]",
|
|
86
|
+
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]",
|
|
87
|
+
" ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]",
|
|
87
88
|
" ouro auth verify [--agent <name>] [--provider <provider>]",
|
|
88
89
|
" ouro auth switch [--agent <name>] --provider <provider>",
|
|
89
90
|
" ouro vault create [--agent <name>] --email <email> [--server <url>] [--store <store>]",
|
|
@@ -600,15 +601,53 @@ function normalizeConnectTarget(value) {
|
|
|
600
601
|
return "teams";
|
|
601
602
|
if (value === "bluebubbles" || value === "imessage" || value === "messages")
|
|
602
603
|
return "bluebubbles";
|
|
603
|
-
|
|
604
|
+
if (value === "mail" || value === "email" || value === "mailroom")
|
|
605
|
+
return "mail";
|
|
606
|
+
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]");
|
|
604
607
|
}
|
|
605
608
|
function parseConnectCommand(args) {
|
|
606
609
|
const { agent, rest } = extractAgentFlag(args);
|
|
607
610
|
if (rest.length > 1)
|
|
608
|
-
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles] [--agent <name>]");
|
|
611
|
+
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]");
|
|
609
612
|
const target = normalizeConnectTarget(rest[0]);
|
|
610
613
|
return { kind: "connect", ...(agent ? { agent } : {}), ...(target ? { target } : {}) };
|
|
611
614
|
}
|
|
615
|
+
function parseMailCommand(args) {
|
|
616
|
+
const [sub, ...subArgs] = args;
|
|
617
|
+
if (sub !== "import-mbox") {
|
|
618
|
+
throw new Error("Usage: ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]");
|
|
619
|
+
}
|
|
620
|
+
const { agent, rest } = extractAgentFlag(subArgs);
|
|
621
|
+
let filePath;
|
|
622
|
+
let ownerEmail;
|
|
623
|
+
let source;
|
|
624
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
625
|
+
const token = rest[i];
|
|
626
|
+
if (token === "--file" && rest[i + 1]) {
|
|
627
|
+
filePath = rest[++i];
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
if (token === "--owner-email" && rest[i + 1]) {
|
|
631
|
+
ownerEmail = rest[++i];
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
if (token === "--source" && rest[i + 1]) {
|
|
635
|
+
source = rest[++i];
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
throw new Error("Usage: ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]");
|
|
639
|
+
}
|
|
640
|
+
if (!filePath) {
|
|
641
|
+
throw new Error("Usage: ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>]");
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
kind: "mail.import-mbox",
|
|
645
|
+
...(agent ? { agent } : {}),
|
|
646
|
+
filePath,
|
|
647
|
+
...(ownerEmail ? { ownerEmail } : {}),
|
|
648
|
+
...(source ? { source } : {}),
|
|
649
|
+
};
|
|
650
|
+
}
|
|
612
651
|
function parseProviderUseCommand(args) {
|
|
613
652
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
614
653
|
const { facing, rest: afterFacing } = extractFacingFlag(afterAgent);
|
|
@@ -1078,6 +1117,8 @@ function parseOuroCommand(args) {
|
|
|
1078
1117
|
}
|
|
1079
1118
|
if (head === "provider")
|
|
1080
1119
|
return parseProviderCommand(args.slice(1));
|
|
1120
|
+
if (head === "mail")
|
|
1121
|
+
return parseMailCommand(args.slice(1));
|
|
1081
1122
|
if (head === "logs") {
|
|
1082
1123
|
if (second === "prune")
|
|
1083
1124
|
return { kind: "daemon.logs.prune" };
|
|
@@ -181,7 +181,7 @@ async function checkSenses(deps) {
|
|
|
181
181
|
continue;
|
|
182
182
|
}
|
|
183
183
|
const senses = config.senses;
|
|
184
|
-
const senseNames = ["cli", "teams", "bluebubbles"];
|
|
184
|
+
const senseNames = ["cli", "teams", "bluebubbles", "mail"];
|
|
185
185
|
for (const sense of senseNames) {
|
|
186
186
|
if (!(sense in senses))
|
|
187
187
|
continue;
|
|
@@ -52,6 +52,7 @@ function defaultSenses() {
|
|
|
52
52
|
cli: { ...identity_1.DEFAULT_AGENT_SENSES.cli },
|
|
53
53
|
teams: { ...identity_1.DEFAULT_AGENT_SENSES.teams },
|
|
54
54
|
bluebubbles: { ...identity_1.DEFAULT_AGENT_SENSES.bluebubbles },
|
|
55
|
+
mail: { ...identity_1.DEFAULT_AGENT_SENSES.mail },
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
function readAgentSenses(agentJsonPath) {
|
|
@@ -77,7 +78,7 @@ function readAgentSenses(agentJsonPath) {
|
|
|
77
78
|
if (!rawSenses || typeof rawSenses !== "object" || Array.isArray(rawSenses)) {
|
|
78
79
|
return defaults;
|
|
79
80
|
}
|
|
80
|
-
for (const sense of ["cli", "teams", "bluebubbles"]) {
|
|
81
|
+
for (const sense of ["cli", "teams", "bluebubbles", "mail"]) {
|
|
81
82
|
const rawSense = rawSenses[sense];
|
|
82
83
|
if (!rawSense || typeof rawSense !== "object" || Array.isArray(rawSense)) {
|
|
83
84
|
continue;
|
|
@@ -117,6 +118,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
117
118
|
cli: { configured: true, detail: "local interactive terminal" },
|
|
118
119
|
teams: { configured: false, detail: "not enabled in agent.json" },
|
|
119
120
|
bluebubbles: { configured: false, detail: "not enabled in agent.json" },
|
|
121
|
+
mail: { configured: false, detail: "not enabled in agent.json" },
|
|
120
122
|
};
|
|
121
123
|
const payload = runtimeConfig.ok ? runtimeConfig.config : {};
|
|
122
124
|
const unavailableDetail = runtimeConfigUnavailableDetail(agent, runtimeConfig);
|
|
@@ -125,6 +127,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
125
127
|
const machinePayload = machineRuntimeConfig.ok ? machineRuntimeConfig.config : {};
|
|
126
128
|
const bluebubbles = machinePayload.bluebubbles;
|
|
127
129
|
const bluebubblesChannel = machinePayload.bluebubblesChannel;
|
|
130
|
+
const mailroom = payload.mailroom;
|
|
128
131
|
if (senses.teams.enabled) {
|
|
129
132
|
const missing = [];
|
|
130
133
|
if (!textField(teams, "clientId"))
|
|
@@ -166,12 +169,33 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
166
169
|
: runtimeConfigUnavailableDetail(agent, machineRuntimeConfig),
|
|
167
170
|
};
|
|
168
171
|
}
|
|
172
|
+
if (senses.mail.enabled) {
|
|
173
|
+
const privateKeys = mailroom?.privateKeys;
|
|
174
|
+
const hasPrivateKeys = !!privateKeys && typeof privateKeys === "object" && !Array.isArray(privateKeys) && Object.values(privateKeys).some((value) => typeof value === "string" && value.trim().length > 0);
|
|
175
|
+
const mailboxAddress = textField(mailroom, "mailboxAddress");
|
|
176
|
+
const missing = [];
|
|
177
|
+
if (!mailboxAddress)
|
|
178
|
+
missing.push("mailroom.mailboxAddress");
|
|
179
|
+
if (!hasPrivateKeys)
|
|
180
|
+
missing.push("mailroom.privateKeys");
|
|
181
|
+
base.mail = missing.length === 0
|
|
182
|
+
? { configured: true, detail: mailboxAddress }
|
|
183
|
+
: {
|
|
184
|
+
configured: false,
|
|
185
|
+
detail: runtimeConfig.ok
|
|
186
|
+
? `missing ${missing.join("/")}`
|
|
187
|
+
: unavailableDetail,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
169
190
|
return base;
|
|
170
191
|
}
|
|
171
192
|
function senseRepairHint(agent, sense) {
|
|
172
193
|
if (sense === "teams") {
|
|
173
194
|
return `Run 'ouro vault config set --agent ${agent} --key teams.clientId', teams.clientSecret, and teams.tenantId; then run 'ouro up' again.`;
|
|
174
195
|
}
|
|
196
|
+
if (sense === "mail") {
|
|
197
|
+
return `Run 'ouro connect mail --agent ${agent}' to provision Mailroom access; then run 'ouro up' again.`;
|
|
198
|
+
}
|
|
175
199
|
return `Run 'ouro connect bluebubbles --agent ${agent}' to attach BlueBubbles on this machine; then run 'ouro up' again.`;
|
|
176
200
|
}
|
|
177
201
|
function currentMachineId() {
|
|
@@ -182,7 +206,7 @@ function parseSenseSnapshotName(name) {
|
|
|
182
206
|
if (parts.length !== 2)
|
|
183
207
|
return null;
|
|
184
208
|
const [agent, sense] = parts;
|
|
185
|
-
if (sense !== "teams" && sense !== "bluebubbles")
|
|
209
|
+
if (sense !== "teams" && sense !== "bluebubbles" && sense !== "mail")
|
|
186
210
|
return null;
|
|
187
211
|
return { agent, sense };
|
|
188
212
|
}
|
|
@@ -345,6 +369,9 @@ class DaemonSenseManager {
|
|
|
345
369
|
optional: context.facts.bluebubbles.optional,
|
|
346
370
|
...blueBubblesRuntimeFacts,
|
|
347
371
|
},
|
|
372
|
+
mail: {
|
|
373
|
+
configured: context.facts.mail.configured,
|
|
374
|
+
},
|
|
348
375
|
};
|
|
349
376
|
const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
|
|
350
377
|
return inventory.map((entry) => ({
|
package/dist/heart/identity.js
CHANGED
|
@@ -134,12 +134,14 @@ exports.DEFAULT_AGENT_SENSES = {
|
|
|
134
134
|
cli: { enabled: true },
|
|
135
135
|
teams: { enabled: false },
|
|
136
136
|
bluebubbles: { enabled: false },
|
|
137
|
+
mail: { enabled: false },
|
|
137
138
|
};
|
|
138
139
|
function normalizeSenses(value, configFile) {
|
|
139
140
|
const defaults = {
|
|
140
141
|
cli: { ...exports.DEFAULT_AGENT_SENSES.cli },
|
|
141
142
|
teams: { ...exports.DEFAULT_AGENT_SENSES.teams },
|
|
142
143
|
bluebubbles: { ...exports.DEFAULT_AGENT_SENSES.bluebubbles },
|
|
144
|
+
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
143
145
|
};
|
|
144
146
|
if (value === undefined) {
|
|
145
147
|
return defaults;
|
|
@@ -155,7 +157,7 @@ function normalizeSenses(value, configFile) {
|
|
|
155
157
|
throw new Error(`agent.json at ${configFile} must include senses as an object when present.`);
|
|
156
158
|
}
|
|
157
159
|
const raw = value;
|
|
158
|
-
const senseNames = ["cli", "teams", "bluebubbles"];
|
|
160
|
+
const senseNames = ["cli", "teams", "bluebubbles", "mail"];
|
|
159
161
|
for (const senseName of senseNames) {
|
|
160
162
|
const rawSense = raw[senseName];
|
|
161
163
|
if (rawSense === undefined) {
|
|
@@ -197,6 +199,7 @@ function buildDefaultAgentTemplate(_agentName) {
|
|
|
197
199
|
cli: { ...exports.DEFAULT_AGENT_SENSES.cli },
|
|
198
200
|
teams: { ...exports.DEFAULT_AGENT_SENSES.teams },
|
|
199
201
|
bluebubbles: { ...exports.DEFAULT_AGENT_SENSES.bluebubbles },
|
|
202
|
+
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
200
203
|
},
|
|
201
204
|
phrases: {
|
|
202
205
|
thinking: [...exports.DEFAULT_AGENT_PHRASES.thinking],
|
|
@@ -56,6 +56,8 @@ function createOutlookHttpReadHooks(options) {
|
|
|
56
56
|
readAgentSelfFix: options.readAgentSelfFix ?? ((agentName) => (0, outlook_read_1.readSelfFixView)(agentRoot(agentName))),
|
|
57
57
|
readAgentNoteDecisions: options.readAgentNoteDecisions ?? ((agentName) => (0, outlook_read_1.readNoteDecisionView)(agentRoot(agentName))),
|
|
58
58
|
readAgentHabits: options.readAgentHabits ?? ((agentName) => (0, outlook_read_1.readHabitView)(agentRoot(agentName))),
|
|
59
|
+
readAgentMail: options.readAgentMail ?? ((agentName) => (0, outlook_read_1.readMailView)(agentName)),
|
|
60
|
+
readAgentMailMessage: options.readAgentMailMessage ?? ((agentName, messageId) => (0, outlook_read_1.readMailMessageView)(agentName, messageId)),
|
|
59
61
|
readDaemonHealth: options.readDaemonHealth ?? (() => (0, outlook_read_1.readDaemonHealthDeep)(options.healthPath)),
|
|
60
62
|
readLogs: options.readLogs ?? (() => (0, outlook_read_1.readLogView)(options.logPath ?? null)),
|
|
61
63
|
readDeskPrefs: (agentName) => (0, outlook_read_1.readDeskPrefs)(agentRoot(agentName)),
|
|
@@ -82,10 +82,12 @@ function createOutlookHttpRequestHandler(options) {
|
|
|
82
82
|
}
|
|
83
83
|
const agentMatch = /^\/api\/agents\/([^/]+)(?:\/(.+))?$/.exec(pathname);
|
|
84
84
|
if (agentMatch) {
|
|
85
|
-
handleAgentRoute(request, response, {
|
|
85
|
+
void handleAgentRoute(request, response, {
|
|
86
86
|
agent: decodeURIComponent(agentMatch[1]),
|
|
87
87
|
surface: agentMatch[2] ?? null,
|
|
88
88
|
options,
|
|
89
|
+
}).catch((error) => {
|
|
90
|
+
(0, outlook_http_response_1.writeJson)(response, 500, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
89
91
|
});
|
|
90
92
|
return;
|
|
91
93
|
}
|
|
@@ -97,7 +99,7 @@ function createOutlookHttpRequestHandler(options) {
|
|
|
97
99
|
(0, outlook_http_response_1.writeJson)(response, 404, { ok: false, error: `not found: ${pathname}` });
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
|
-
function handleAgentRoute(request, response, context) {
|
|
102
|
+
async function handleAgentRoute(request, response, context) {
|
|
101
103
|
const { agent, surface, options } = context;
|
|
102
104
|
if (!surface) {
|
|
103
105
|
const view = options.readAgentView?.(agent);
|
|
@@ -190,6 +192,16 @@ function handleAgentRoute(request, response, context) {
|
|
|
190
192
|
(0, outlook_http_response_1.writeJson)(response, 200, options.hooks.readAgentHabits(agent));
|
|
191
193
|
return;
|
|
192
194
|
}
|
|
195
|
+
if (surface === "mail") {
|
|
196
|
+
(0, outlook_http_response_1.writeJson)(response, 200, await options.hooks.readAgentMail(agent));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const mailMessageMatch = /^mail\/([^/]+)$/.exec(surface);
|
|
200
|
+
if (mailMessageMatch) {
|
|
201
|
+
const messageId = decodeURIComponent(mailMessageMatch[1]);
|
|
202
|
+
(0, outlook_http_response_1.writeJson)(response, 200, await options.hooks.readAgentMailMessage(agent, messageId));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
193
205
|
if (surface === "inner-transcript") {
|
|
194
206
|
const transcript = options.hooks.readAgentTranscript(agent, "self", "inner", "dialog");
|
|
195
207
|
(0, outlook_http_response_1.writeJson)(response, 200, transcript ?? { messageCount: 0, messages: [] });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readSelfFixView = exports.readOutlookContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readHabitView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readSessionTranscript = exports.readSessionInventory = exports.readOutlookMachineState = exports.readOutlookAgentState = exports.readObligationSummary = void 0;
|
|
3
|
+
exports.readSelfFixView = exports.readOutlookContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readHabitView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readMailView = exports.readMailMessageView = exports.readSessionTranscript = exports.readSessionInventory = exports.readOutlookMachineState = exports.readOutlookAgentState = exports.readObligationSummary = void 0;
|
|
4
4
|
var agent_machine_1 = require("./readers/agent-machine");
|
|
5
5
|
Object.defineProperty(exports, "readObligationSummary", { enumerable: true, get: function () { return agent_machine_1.readObligationSummary; } });
|
|
6
6
|
Object.defineProperty(exports, "readOutlookAgentState", { enumerable: true, get: function () { return agent_machine_1.readOutlookAgentState; } });
|
|
@@ -8,6 +8,9 @@ Object.defineProperty(exports, "readOutlookMachineState", { enumerable: true, ge
|
|
|
8
8
|
var sessions_1 = require("./readers/sessions");
|
|
9
9
|
Object.defineProperty(exports, "readSessionInventory", { enumerable: true, get: function () { return sessions_1.readSessionInventory; } });
|
|
10
10
|
Object.defineProperty(exports, "readSessionTranscript", { enumerable: true, get: function () { return sessions_1.readSessionTranscript; } });
|
|
11
|
+
var mail_1 = require("./readers/mail");
|
|
12
|
+
Object.defineProperty(exports, "readMailMessageView", { enumerable: true, get: function () { return mail_1.readMailMessageView; } });
|
|
13
|
+
Object.defineProperty(exports, "readMailView", { enumerable: true, get: function () { return mail_1.readMailView; } });
|
|
11
14
|
var runtime_readers_1 = require("./readers/runtime-readers");
|
|
12
15
|
Object.defineProperty(exports, "readAttentionView", { enumerable: true, get: function () { return runtime_readers_1.readAttentionView; } });
|
|
13
16
|
Object.defineProperty(exports, "readBridgeInventory", { enumerable: true, get: function () { return runtime_readers_1.readBridgeInventory; } });
|