humanish 0.94.0 → 0.96.0
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/README.md +10 -0
- package/dist/comms-agentmail.d.ts +25 -0
- package/dist/comms-agentmail.js +418 -0
- package/dist/comms-agentmail.js.map +1 -0
- package/dist/comms-connections.d.ts +52 -0
- package/dist/comms-connections.js +123 -0
- package/dist/comms-connections.js.map +1 -0
- package/dist/comms-lease-store.d.ts +90 -0
- package/dist/comms-lease-store.js +489 -0
- package/dist/comms-lease-store.js.map +1 -0
- package/dist/comms-receiving-evidence.d.ts +46 -0
- package/dist/comms-receiving-evidence.js +35 -0
- package/dist/comms-receiving-evidence.js.map +1 -0
- package/dist/comms-receiving-inbox.d.ts +16 -0
- package/dist/comms-receiving-inbox.js +410 -0
- package/dist/comms-receiving-inbox.js.map +1 -0
- package/dist/comms-receiving-runtime.d.ts +25 -0
- package/dist/comms-receiving-runtime.js +45 -0
- package/dist/comms-receiving-runtime.js.map +1 -0
- package/dist/comms-receiving-types.d.ts +98 -0
- package/dist/comms-receiving-types.js +2 -0
- package/dist/comms-receiving-types.js.map +1 -0
- package/dist/comms-receiving.d.ts +58 -0
- package/dist/comms-receiving.js +554 -0
- package/dist/comms-receiving.js.map +1 -0
- package/dist/comms-setup.d.ts +40 -0
- package/dist/comms-setup.js +121 -0
- package/dist/comms-setup.js.map +1 -0
- package/dist/concurrent-shared-world-lab.js +662 -624
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +4 -1
- package/dist/cua-actor-lab.js +98 -17
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/doctor-lab.d.ts +7 -0
- package/dist/doctor-lab.js +26 -7
- package/dist/doctor-lab.js.map +1 -1
- package/dist/e2b-terminal-lab.js +3 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/key-resolution.d.ts +1 -1
- package/dist/key-resolution.js +6 -4
- package/dist/key-resolution.js.map +1 -1
- package/dist/lab-config.d.ts +20 -3
- package/dist/lab-config.js +65 -3
- package/dist/lab-config.js.map +1 -1
- package/dist/lab-engine.js +8 -3
- package/dist/lab-engine.js.map +1 -1
- package/dist/lab-summary.d.ts +3 -2
- package/dist/lab-summary.js +17 -7
- package/dist/lab-summary.js.map +1 -1
- package/dist/observer-app.html +1 -1
- package/dist/observer-data.js +14 -1
- package/dist/observer-data.js.map +1 -1
- package/dist/oss-lab.d.ts +1 -1
- package/dist/oss-lab.js.map +1 -1
- package/dist/oss-meta-lab.d.ts +1 -1
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/program.d.ts +4 -0
- package/dist/program.js +231 -74
- package/dist/program.js.map +1 -1
- package/dist/run-narration-secrets.d.ts +5 -0
- package/dist/run-narration-secrets.js +68 -0
- package/dist/run-narration-secrets.js.map +1 -0
- package/dist/run.d.ts +7 -2
- package/dist/run.js +25 -3
- package/dist/run.js.map +1 -1
- package/dist/scripted-browser-lab.js +7 -0
- package/dist/scripted-browser-lab.js.map +1 -1
- package/dist/secret-prompt.d.ts +2 -0
- package/dist/secret-prompt.js +36 -0
- package/dist/secret-prompt.js.map +1 -0
- package/dist/shared-world-lab.js +4 -0
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/study-analysis-engine.js +30 -1
- package/dist/study-analysis-engine.js.map +1 -1
- package/dist/study-analysis-evidence.js +9 -1
- package/dist/study-analysis-evidence.js.map +1 -1
- package/dist/tui-app.js +135 -135
- package/dist/tui-contract.d.ts +26 -1
- package/dist/tui-contract.js.map +1 -1
- package/dist/tui-launch.d.ts +2 -0
- package/dist/tui-launch.js +9 -1
- package/dist/tui-launch.js.map +1 -1
- package/docs/architecture/comms-inbox.md +4 -0
- package/docs/architecture/real-email-receiving.md +135 -0
- package/docs/contracts/run-bundle.md +9 -1
- package/docs/contracts/schemas.md +50 -5
- package/docs/goals/current.md +5 -5
- package/docs/ramp/README.md +13 -3
- package/docs/release/0.95.0-connections-setup.md +38 -0
- package/docs/release/0.96.0-real-email-receiving.md +47 -0
- package/package.json +4 -2
- package/skills/humanish/SKILL.md +55 -14
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/** Participant-local real email. Raw content stays in memory and the participant's desktop. */
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { parseFragment } from "parse5";
|
|
4
|
+
import { capturedInlineImages, inlineImageData, MAX_INLINE_IMAGES, MAX_INLINE_IMAGES_BYTES } from "./comms-images.js";
|
|
5
|
+
import { extractLinks, extractOtpCodes } from "./comms-fake-inbox.js";
|
|
6
|
+
export const RECEIVING_INBOX_CSP = "default-src 'none'; script-src 'none'; connect-src 'none'; img-src data:; style-src 'unsafe-inline'; font-src 'none'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'";
|
|
7
|
+
const MAX_SNAPSHOT_BYTES = 32 * 1024 * 1024;
|
|
8
|
+
const MAX_MESSAGE_BYTES = 256 * 1024;
|
|
9
|
+
const ID = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
10
|
+
const ROUTE = /^inbox(?:\.json|\/(?:[a-zA-Z0-9][a-zA-Z0-9_-]{0,63})(?:\/plain|\.json)?)?$/;
|
|
11
|
+
const TAGS = new Set("a abbr b blockquote br caption center code col colgroup dd div dl dt em h1 h2 h3 h4 h5 h6 hr i img li ol p pre s small span strong sub sup table tbody td th thead tfoot tr u ul".split(" "));
|
|
12
|
+
const DROP = new Set("script style iframe frame frameset object embed svg math template noscript textarea select button input video audio source track canvas meta base link".split(" "));
|
|
13
|
+
const VOID = new Set(["br", "hr", "col", "img"]);
|
|
14
|
+
const CSS = "body{font:16px/1.55 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#111;background:#fff;margin:0}a{color:#0645ad;text-underline-offset:3px}a:focus-visible{outline:3px solid #225acb;outline-offset:4px}.bar{padding:12px 20px;background:#f4f4f4;border-bottom:1px solid #ccc;font-size:14px}.bar a{display:inline-block;margin-right:20px;min-height:28px}.hdr{padding:16px 20px;border-bottom:3px solid #111;overflow-wrap:anywhere}.hdr h1{font-size:24px;line-height:1.25;margin:0 0 10px}.hdr div{margin:2px 0}.hdr b{display:inline-block;min-width:70px;color:#444}.body{padding:20px;max-width:860px;overflow-wrap:anywhere}img{max-width:100%;height:auto}pre{white-space:pre-wrap;overflow-wrap:anywhere}table{border-collapse:collapse;max-width:100%;width:100%}th,td{text-align:left;padding:12px 16px;border-bottom:1px solid #ddd;overflow-wrap:anywhere}th{border-bottom:2px solid #111}.inbox-list a{display:inline-block;min-height:32px;font-weight:600}.notice,.blocked-image{display:block;padding:12px;border:1px solid #bbb;background:#f7f7f7;color:#444;font-size:14px}.blocked-link{color:#444}.muted{color:#555;font-size:14px}.email-body{isolation:isolate}.email-body table{width:auto}.email-body img{object-fit:contain}.otp{font:28px/1.4 ui-monospace,monospace;letter-spacing:4px;background:#f0f0f0;padding:8px 12px;display:inline-block}.cta{display:inline-block;padding:12px 20px;color:white;background:#111;border-radius:6px;text-decoration:none}@media(max-width:560px){.inbox-list thead{display:none}.inbox-list,.inbox-list tbody,.inbox-list tr,.inbox-list td{display:block}.inbox-list tr{padding:10px 0;border-bottom:1px solid #ddd}.inbox-list td{border:0;padding:2px 0}.body{padding:16px}.hdr,.bar{padding:12px 16px}}";
|
|
15
|
+
function esc(value) {
|
|
16
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17
|
+
}
|
|
18
|
+
function page(title, body) {
|
|
19
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><meta http-equiv="Content-Security-Policy" content="${RECEIVING_INBOX_CSP}"><title>${esc(title)}</title><style>${CSS}</style></head><body>${body}</body></html>`;
|
|
20
|
+
}
|
|
21
|
+
function origin(value) {
|
|
22
|
+
try {
|
|
23
|
+
const u = new URL(value);
|
|
24
|
+
return /^(https?:)$/.test(u.protocol) && !u.username && !u.password ? u.origin : undefined;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Allowlisted presentation values only. No CSS escapes, functions (except rgb), positioning or URLs. */
|
|
31
|
+
function safeStyle(style) {
|
|
32
|
+
const declarations = [];
|
|
33
|
+
for (const item of style.split(";")) {
|
|
34
|
+
const colon = item.indexOf(":");
|
|
35
|
+
if (colon < 1)
|
|
36
|
+
continue;
|
|
37
|
+
let name = item.slice(0, colon).trim().toLowerCase();
|
|
38
|
+
const value = item.slice(colon + 1).trim().toLowerCase();
|
|
39
|
+
if (value.length > 120)
|
|
40
|
+
continue;
|
|
41
|
+
const color = /^(?:#[a-f0-9]{3,8}|[a-z]{3,20}|rgba?\([\d.,% ]{1,35}\))$/;
|
|
42
|
+
const size = /^(?:0|\d{1,3}(?:\.\d{1,2})?(?:px|em|rem|%))(?: (?:0|\d{1,3}(?:\.\d{1,2})?(?:px|em|rem|%))){0,3}$/;
|
|
43
|
+
// A color-only shorthand is common on email CTAs. Dropping it while retaining white text
|
|
44
|
+
// makes a working confirmation link invisible. Complex/image backgrounds remain unsupported.
|
|
45
|
+
if (name === "background" && color.test(value))
|
|
46
|
+
name = "background-color";
|
|
47
|
+
if ((["color", "background-color", "border-color"].includes(name) && color.test(value)) ||
|
|
48
|
+
(["padding", "padding-top", "padding-bottom", "padding-left", "padding-right", "margin", "margin-top", "margin-bottom", "margin-left", "margin-right", "font-size", "border-radius", "border-width", "max-width", "width", "height"].includes(name) && size.test(value)) ||
|
|
49
|
+
(name === "text-align" && /^(left|right|center|justify)$/.test(value)) ||
|
|
50
|
+
(name === "display" && /^(inline|inline-block|block)$/.test(value)) ||
|
|
51
|
+
(name === "font-weight" && /^(normal|bold|[1-9]00)$/.test(value)) ||
|
|
52
|
+
(name === "font-style" && /^(normal|italic)$/.test(value)) ||
|
|
53
|
+
(name === "text-decoration" && /^(none|underline|line-through)$/.test(value)) ||
|
|
54
|
+
(name === "line-height" && /^(?:[12](?:\.\d{1,2})?|normal)$/.test(value)) ||
|
|
55
|
+
(name === "border-style" && /^(none|solid|dashed|dotted)$/.test(value)) ||
|
|
56
|
+
(name === "font-family" && /^[a-z ,'-]{1,90}$/.test(value)))
|
|
57
|
+
declarations.push(`${name}:${value}`);
|
|
58
|
+
}
|
|
59
|
+
return declarations.join(";");
|
|
60
|
+
}
|
|
61
|
+
/** Totals describe the whole snapshot (not increments). Register secrets BEFORE publishing files. */
|
|
62
|
+
export function renderReceivingInbox(options) {
|
|
63
|
+
if (!options.address || options.address.length > 512 || options.messages.length > 100)
|
|
64
|
+
throw new Error("Receiving inbox input exceeds limits.");
|
|
65
|
+
const allowed = new Set(options.allowedOrigins.map(origin).filter((o) => !!o));
|
|
66
|
+
const rewrites = (options.originMap ?? []).map(([from, to]) => [origin(from), origin(to)]);
|
|
67
|
+
const secrets = new Set([options.address]);
|
|
68
|
+
const allLinks = new Set(), allCodes = new Set();
|
|
69
|
+
const ids = new Set();
|
|
70
|
+
let blockedAssetCount = 0, blockedLinkCount = 0;
|
|
71
|
+
function link(raw) {
|
|
72
|
+
if (raw)
|
|
73
|
+
secrets.add(raw);
|
|
74
|
+
if (raw.length > 8192 || /[\u0000-\u0020\u007f\\]/.test(raw))
|
|
75
|
+
return undefined;
|
|
76
|
+
try {
|
|
77
|
+
const u = new URL(raw);
|
|
78
|
+
if (!/^(https?:)$/.test(u.protocol) || u.username || u.password)
|
|
79
|
+
return undefined;
|
|
80
|
+
secrets.add(u.href);
|
|
81
|
+
for (const [key, value] of u.searchParams)
|
|
82
|
+
if (/token|code|secret|password|auth|invite|verification|key/i.test(key) && value.length >= 4)
|
|
83
|
+
secrets.add(value);
|
|
84
|
+
const mapped = rewrites.find(([from, to]) => from === u.origin && to !== undefined)?.[1];
|
|
85
|
+
const target = mapped ? new URL(u.pathname + u.search + u.hash, `${mapped}/`) : u;
|
|
86
|
+
// Never decode paths/queries into a second navigation target or follow redirects here.
|
|
87
|
+
secrets.add(target.href);
|
|
88
|
+
if (!allowed.has(target.origin))
|
|
89
|
+
return undefined;
|
|
90
|
+
return target.href;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function content(message) {
|
|
97
|
+
for (const value of [message.text, message.html ?? ""])
|
|
98
|
+
if (Buffer.byteLength(value) > MAX_MESSAGE_BYTES)
|
|
99
|
+
throw new Error("Receiving inbox message exceeds limits.");
|
|
100
|
+
if (message.from.length > 2048 || (message.subject?.length ?? 0) > 4096)
|
|
101
|
+
throw new Error("Receiving inbox metadata exceeds limits.");
|
|
102
|
+
secrets.add(message.from);
|
|
103
|
+
if (message.subject)
|
|
104
|
+
secrets.add(message.subject);
|
|
105
|
+
for (const match of `${message.from}\n${message.subject ?? ""}\n${message.text}\n${message.html ?? ""}`.matchAll(/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g))
|
|
106
|
+
secrets.add(match[0]);
|
|
107
|
+
const images = capturedInlineImages(message.inlineImages);
|
|
108
|
+
const links = new Set(), blocked = new Set();
|
|
109
|
+
let assets = 0, nodes = 0, displayedImages = 0, displayedImageBytes = 0;
|
|
110
|
+
const parsed = parseFragment(message.html ?? "");
|
|
111
|
+
const visibleText = [];
|
|
112
|
+
function render(node, depth) {
|
|
113
|
+
if (++nodes > 20000 || depth > 100)
|
|
114
|
+
throw new Error("Receiving inbox HTML exceeds limits.");
|
|
115
|
+
if ("value" in node) {
|
|
116
|
+
visibleText.push(node.value);
|
|
117
|
+
for (const match of node.value.matchAll(/https?:\/\/[^\s"'<>]+/gi))
|
|
118
|
+
secrets.add(match[0]);
|
|
119
|
+
return esc(node.value);
|
|
120
|
+
}
|
|
121
|
+
if (!("tagName" in node))
|
|
122
|
+
return "";
|
|
123
|
+
const tag = node.tagName;
|
|
124
|
+
const attrs = new Map(node.attrs.map((a) => [a.name, a.value]));
|
|
125
|
+
// These strings become scrub targets even when the element is dropped.
|
|
126
|
+
for (const attr of node.attrs)
|
|
127
|
+
for (const url of extractLinks(attr.value))
|
|
128
|
+
secrets.add(url);
|
|
129
|
+
if (attrs.get("style")?.match(/url\s*\(|@import/i))
|
|
130
|
+
assets++;
|
|
131
|
+
for (const name of ["background", "srcset", "poster"])
|
|
132
|
+
if (attrs.get(name))
|
|
133
|
+
assets++;
|
|
134
|
+
if (node.namespaceURI !== "http://www.w3.org/1999/xhtml" || DROP.has(tag)) {
|
|
135
|
+
if (["iframe", "frame", "object", "embed", "source", "video", "audio", "link", "style", "svg"].includes(tag))
|
|
136
|
+
assets++;
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
if (tag === "img") {
|
|
140
|
+
const src = (attrs.get("src") ?? "").trim();
|
|
141
|
+
let data;
|
|
142
|
+
if (/^cid:/i.test(src)) {
|
|
143
|
+
let cid = "";
|
|
144
|
+
try {
|
|
145
|
+
cid = decodeURIComponent(src.slice(4)).replace(/^<|>$/g, "");
|
|
146
|
+
}
|
|
147
|
+
catch { /* unsupported content id */ }
|
|
148
|
+
const matches = images.filter((image) => image.contentId === cid);
|
|
149
|
+
if (matches.length === 1)
|
|
150
|
+
data = inlineImageData(matches[0]);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const match = /^data:(image\/[a-z]+);base64,([A-Za-z0-9+/=]+)$/i.exec(src);
|
|
154
|
+
if (match)
|
|
155
|
+
data = inlineImageData({ contentId: "inline", contentType: match[1].toLowerCase(), base64: match[2] });
|
|
156
|
+
}
|
|
157
|
+
if (data) {
|
|
158
|
+
displayedImages++;
|
|
159
|
+
displayedImageBytes += Buffer.byteLength(data);
|
|
160
|
+
if (displayedImages > MAX_INLINE_IMAGES || displayedImageBytes > Math.ceil(MAX_INLINE_IMAGES_BYTES / 3) * 4 + 1024)
|
|
161
|
+
data = undefined;
|
|
162
|
+
}
|
|
163
|
+
const alt = attrs.get("alt")?.slice(0, 1024) || "Email image";
|
|
164
|
+
if (!data) {
|
|
165
|
+
assets++;
|
|
166
|
+
return `<span class="blocked-image" role="img" aria-label="${esc(alt)}">${esc(alt)} — Image unavailable. Remote images are blocked; embedded images must be supported and within the size limit.</span>`;
|
|
167
|
+
}
|
|
168
|
+
// srcset, background and all network-bearing image attributes are deliberately absent.
|
|
169
|
+
const width = /^\d{1,3}$/.test(attrs.get("width") ?? "") ? ` width="${attrs.get("width")}"` : "";
|
|
170
|
+
const style = safeStyle(attrs.get("style") ?? "");
|
|
171
|
+
return `<img src="${data}" alt="${esc(alt)}"${width}${style ? ` style="${esc(style)}"` : ""}>`;
|
|
172
|
+
}
|
|
173
|
+
const children = node.childNodes.map((child) => render(child, depth + 1)).join("");
|
|
174
|
+
if (!TAGS.has(tag))
|
|
175
|
+
return children; // forms are unwrapped; their controls are dropped
|
|
176
|
+
if (tag === "a") {
|
|
177
|
+
const raw = attrs.get("href") ?? "";
|
|
178
|
+
const href = link(raw.trim());
|
|
179
|
+
if (!href) {
|
|
180
|
+
if (raw)
|
|
181
|
+
blocked.add(raw);
|
|
182
|
+
return `<span class="blocked-link">${children} <small>(link unavailable: destination is outside this study or unsupported)</small></span>`;
|
|
183
|
+
}
|
|
184
|
+
links.add(href);
|
|
185
|
+
const style = safeStyle(attrs.get("style") ?? "");
|
|
186
|
+
return `<a href="${esc(href)}" rel="noreferrer noopener" referrerpolicy="no-referrer"${style ? ` style="${esc(style)}"` : ""}>${children || esc(href)}</a>`;
|
|
187
|
+
}
|
|
188
|
+
const style = safeStyle(attrs.get("style") ?? "");
|
|
189
|
+
let safeAttrs = style ? ` style="${esc(style)}"` : "";
|
|
190
|
+
for (const name of ["colspan", "rowspan"])
|
|
191
|
+
if ((tag === "td" || tag === "th") && /^[1-9]\d?$/.test(attrs.get(name) ?? ""))
|
|
192
|
+
safeAttrs += ` ${name}="${attrs.get(name)}"`;
|
|
193
|
+
if (tag === "td" || tag === "th") {
|
|
194
|
+
const background = attrs.get("bgcolor");
|
|
195
|
+
if (background && /^(#[a-f\d]{3,8}|[a-z]{3,20})$/i.test(background))
|
|
196
|
+
safeAttrs += ` bgcolor="${esc(background)}"`;
|
|
197
|
+
}
|
|
198
|
+
return `<${tag}${safeAttrs}>${children}${VOID.has(tag) ? "" : `</${tag}>`}`;
|
|
199
|
+
}
|
|
200
|
+
const html = parsed.childNodes.map((node) => render(node, 0)).join("");
|
|
201
|
+
const text = message.text || visibleText.join(" ");
|
|
202
|
+
// Ports, numeric host labels and token/query fragments are not OTP evidence. Full links and
|
|
203
|
+
// token query values still enter the scrub registry through link(), independently of codes.
|
|
204
|
+
const codeSource = `${message.subject ?? ""}\n${text}\n${visibleText.join(" ")}`.replace(/https?:\/\/[^\s<>"']+/gi, " ");
|
|
205
|
+
const codes = extractOtpCodes(esc(codeSource));
|
|
206
|
+
for (const code of codes) {
|
|
207
|
+
secrets.add(code);
|
|
208
|
+
secrets.add(code.toLowerCase());
|
|
209
|
+
allCodes.add(code);
|
|
210
|
+
for (const match of codeSource.matchAll(new RegExp(code, "gi")))
|
|
211
|
+
secrets.add(match[0]);
|
|
212
|
+
}
|
|
213
|
+
for (const raw of extractLinks(message.html ?? ""))
|
|
214
|
+
secrets.add(raw);
|
|
215
|
+
let cursor = 0, plainHtml = "", normalizedText = "";
|
|
216
|
+
// One parsing policy governs plain links, original anchors and JSON's actionable links.
|
|
217
|
+
const matcher = /https?:\/\/[^\s<>"'\])]+/gi;
|
|
218
|
+
for (const match of text.matchAll(matcher)) {
|
|
219
|
+
const raw = match[0].replace(/[.,;!?]+$/, ""), start = match.index;
|
|
220
|
+
plainHtml += esc(text.slice(cursor, start));
|
|
221
|
+
normalizedText += text.slice(cursor, start);
|
|
222
|
+
const href = link(raw);
|
|
223
|
+
if (href) {
|
|
224
|
+
links.add(href);
|
|
225
|
+
plainHtml += `<a href="${esc(href)}" rel="noreferrer noopener" referrerpolicy="no-referrer">${esc(href)}</a>`;
|
|
226
|
+
normalizedText += href;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
blocked.add(raw);
|
|
230
|
+
plainHtml += '<span class="blocked-link">[link unavailable: destination is outside this study or unsupported]</span>';
|
|
231
|
+
normalizedText += "[link unavailable: destination is outside this study or unsupported]";
|
|
232
|
+
}
|
|
233
|
+
cursor = start + raw.length;
|
|
234
|
+
}
|
|
235
|
+
plainHtml += esc(text.slice(cursor));
|
|
236
|
+
normalizedText += text.slice(cursor);
|
|
237
|
+
for (const href of links)
|
|
238
|
+
allLinks.add(href);
|
|
239
|
+
return { html, plainHtml, text: normalizedText, links: [...links], codes, blockedAssetCount: assets, blockedLinkCount: blocked.size };
|
|
240
|
+
}
|
|
241
|
+
const files = [];
|
|
242
|
+
let fileBytes = 0;
|
|
243
|
+
const add = (path, body, json = false) => {
|
|
244
|
+
fileBytes += Buffer.byteLength(body);
|
|
245
|
+
if (fileBytes > MAX_SNAPSHOT_BYTES)
|
|
246
|
+
throw new Error("Receiving inbox snapshot exceeds limits.");
|
|
247
|
+
files.push({ path, body, contentType: json ? "application/json; charset=utf-8" : "text/html; charset=utf-8" });
|
|
248
|
+
};
|
|
249
|
+
const rendered = options.messages.map((message) => {
|
|
250
|
+
if (!ID.test(message.id) || message.id === "latest" || ids.has(message.id))
|
|
251
|
+
throw new Error("Receiving inbox requires unique local message IDs.");
|
|
252
|
+
ids.add(message.id);
|
|
253
|
+
const c = content(message);
|
|
254
|
+
blockedAssetCount += c.blockedAssetCount;
|
|
255
|
+
blockedLinkCount += c.blockedLinkCount;
|
|
256
|
+
const head = `<header class="hdr"><h1>${esc(message.subject || "(no subject)")}</h1><div><b>From</b> ${esc(message.from)}</div><div><b>To</b> ${esc(options.address)}</div></header>`;
|
|
257
|
+
const bar = `<nav class="bar" aria-label="Inbox navigation"><a href="/inbox">← Inbox</a><a href="/inbox/${message.id}">Original email</a><a href="/inbox/${message.id}/plain">Plain view</a></nav>`;
|
|
258
|
+
const limitations = c.blockedAssetCount || c.blockedLinkCount ? '<p class="notice">Some images or links were blocked by the study’s email safety policy. This may affect how the email appears.</p>' : "";
|
|
259
|
+
const original = page(message.subject || "Email", bar + head + `<main class="body">${limitations}<div class="email-body">${message.html ? c.html : `<pre>${c.plainHtml}</pre>`}</div></main>`);
|
|
260
|
+
const plain = page(message.subject || "Email", bar + head + `<main class="body">${limitations}${c.links[0] ? `<p><a class="cta" href="${esc(c.links[0])}" rel="noreferrer noopener" referrerpolicy="no-referrer">Open email link</a></p>` : ""}${c.codes[0] ? `<p>Code: <span class="otp">${esc(c.codes[0])}</span></p>` : ""}<pre>${c.plainHtml}</pre></main>`);
|
|
261
|
+
const json = { id: message.id, from: message.from, to: options.address, subject: message.subject ?? "", text: c.text, links: c.links, codes: c.codes, blockedAssetCount: c.blockedAssetCount, blockedLinkCount: c.blockedLinkCount };
|
|
262
|
+
add(`inbox/${message.id}`, original);
|
|
263
|
+
add(`inbox/${message.id}/plain`, plain);
|
|
264
|
+
add(`inbox/${message.id}.json`, JSON.stringify(json), true);
|
|
265
|
+
return { message, original, plain, json };
|
|
266
|
+
});
|
|
267
|
+
const rows = [...rendered].reverse().map(({ message }) => `<tr><td><a href="/inbox/${message.id}">${esc(message.subject || "(no subject)")}</a></td><td>${esc(message.from)}</td></tr>`).join("");
|
|
268
|
+
add("inbox", page("Inbox", `<header class="hdr"><h1>Inbox</h1><div>${esc(options.address)}</div></header><main class="body"><p class="muted">Only messages delivered to your study address appear here. Reload this page to check for new mail.</p>${rows ? `<table class="inbox-list"><thead><tr><th>Subject</th><th>From</th></tr></thead><tbody>${rows}</tbody></table>` : '<p class="notice">No messages yet. Reload after the app sends an email.</p>'}</main>`));
|
|
269
|
+
add("inbox.json", JSON.stringify({ address: options.address, messages: rendered.map((r) => r.json) }), true);
|
|
270
|
+
const latest = rendered.at(-1);
|
|
271
|
+
if (latest) {
|
|
272
|
+
add("inbox/latest", latest.original);
|
|
273
|
+
add("inbox/latest/plain", latest.plain);
|
|
274
|
+
add("inbox/latest.json", JSON.stringify(latest.json), true);
|
|
275
|
+
}
|
|
276
|
+
if (Buffer.byteLength(JSON.stringify(files)) > MAX_SNAPSHOT_BYTES)
|
|
277
|
+
throw new Error("Receiving inbox snapshot exceeds limits.");
|
|
278
|
+
return { files, blockedAssetCount, blockedLinkCount, secrets: [...secrets].filter(Boolean), linkCount: allLinks.size, codeCount: allCodes.size };
|
|
279
|
+
}
|
|
280
|
+
// JSON route map, not a static directory server. Every request reads one atomic snapshot, so removed
|
|
281
|
+
// messages cannot remain reachable. No ingress, directory listing, aggregate inbox or provider IDs.
|
|
282
|
+
const SERVER = `import http.server, json, os, re, sys, socket
|
|
283
|
+
from pathlib import Path
|
|
284
|
+
root, port, nonce, csp = Path(sys.argv[1]), int(sys.argv[2]), sys.argv[3], sys.argv[4]
|
|
285
|
+
class Handler(http.server.BaseHTTPRequestHandler):
|
|
286
|
+
def log_message(self, *args): pass
|
|
287
|
+
def end_headers(self):
|
|
288
|
+
self.send_header('Content-Security-Policy', csp)
|
|
289
|
+
self.send_header('X-Content-Type-Options', 'nosniff')
|
|
290
|
+
self.send_header('Referrer-Policy', 'no-referrer')
|
|
291
|
+
self.send_header('Cache-Control', 'no-store')
|
|
292
|
+
super().end_headers()
|
|
293
|
+
def do_HEAD(self): self.respond(False)
|
|
294
|
+
def do_GET(self): self.respond(True)
|
|
295
|
+
def do_POST(self): self.send_error(405)
|
|
296
|
+
def do_PUT(self): self.send_error(405)
|
|
297
|
+
def do_DELETE(self): self.send_error(405)
|
|
298
|
+
def respond(self, send_body):
|
|
299
|
+
route = self.path
|
|
300
|
+
if self.headers.get('Host') not in ['127.0.0.1:'+str(port), 'localhost:'+str(port)]:
|
|
301
|
+
self.send_error(421); return
|
|
302
|
+
if route == '/health': body, mime = nonce, 'text/plain; charset=utf-8'
|
|
303
|
+
else:
|
|
304
|
+
if route == '/': route = '/inbox'
|
|
305
|
+
try:
|
|
306
|
+
with (root / 'snapshot.json').open('r') as f: snapshot = json.load(f)
|
|
307
|
+
item = snapshot.get('routes', {}).get(route)
|
|
308
|
+
if item is None: self.send_error(404); return
|
|
309
|
+
body, mime = item['body'], item['contentType']
|
|
310
|
+
except Exception:
|
|
311
|
+
self.send_error(503); return
|
|
312
|
+
data = body.encode('utf-8')
|
|
313
|
+
self.send_response(200)
|
|
314
|
+
self.send_header('Content-Type', mime)
|
|
315
|
+
self.send_header('Content-Length', str(len(data)))
|
|
316
|
+
self.end_headers()
|
|
317
|
+
if send_body: self.wfile.write(data)
|
|
318
|
+
class Server(http.server.HTTPServer):
|
|
319
|
+
def get_request(self):
|
|
320
|
+
connection, address = super().get_request(); connection.settimeout(2); return connection, address
|
|
321
|
+
server = Server(('127.0.0.1', port), Handler)
|
|
322
|
+
server.timeout = 0.2
|
|
323
|
+
(root / 'pid').write_text(str(os.getpid()))
|
|
324
|
+
try:
|
|
325
|
+
while not (root / 'stop').exists(): server.handle_request()
|
|
326
|
+
finally: server.server_close()
|
|
327
|
+
`;
|
|
328
|
+
function shq(value) { return `'${value.replaceAll("'", "'\\''")}'`; }
|
|
329
|
+
/** Desktop SDK transport only; never opens a host/public listener or sends management credentials. */
|
|
330
|
+
export async function deployReceivingInbox(desktop, options) {
|
|
331
|
+
const port = options.port ?? 8026, timeout = options.requestTimeoutMs ?? 15000;
|
|
332
|
+
if (!ID.test(options.leaseId) || !Number.isInteger(port) || port < 1024 || port > 65535 || !Number.isInteger(timeout) || timeout < 100 || timeout > 30000)
|
|
333
|
+
throw new Error("Invalid receiving inbox deployment options.");
|
|
334
|
+
const nonce = randomUUID(), dir = `/tmp/humanish-mail-${options.leaseId}-${nonce}`, url = `http://127.0.0.1:${port}/inbox`;
|
|
335
|
+
let stopped = false, generation = 0, tail = Promise.resolve();
|
|
336
|
+
async function bounded(operation) {
|
|
337
|
+
let timer;
|
|
338
|
+
try {
|
|
339
|
+
return await Promise.race([operation, new Promise((_, reject) => { timer = setTimeout(() => reject(new Error("Receiving inbox transport timed out.")), timeout); })]);
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
throw new Error("Receiving inbox transport failed or timed out.");
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
if (timer)
|
|
346
|
+
clearTimeout(timer);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function command(cmd) {
|
|
350
|
+
const result = await bounded(desktop.commands.run(cmd, { requestTimeoutMs: timeout, timeoutMs: timeout }));
|
|
351
|
+
if (result.exitCode !== 0)
|
|
352
|
+
throw new Error("Receiving inbox desktop command failed.");
|
|
353
|
+
return result.stdout ?? "";
|
|
354
|
+
}
|
|
355
|
+
const write = async (path, data) => { await bounded(desktop.files.write(path, data, { requestTimeoutMs: timeout })); };
|
|
356
|
+
const stop = async () => {
|
|
357
|
+
stopped = true;
|
|
358
|
+
await tail.catch(() => undefined);
|
|
359
|
+
// The server owns its process lifetime. A file signal avoids PID-reuse deletion authority.
|
|
360
|
+
await command(`python3 -c ${shq("import pathlib,time,sys,shutil\np=pathlib.Path(sys.argv[1])\nif not p.exists(): sys.exit(0)\n(p/'stop').touch()\nend=time.monotonic()+4\nwhile time.monotonic()<end:\n try:\n pid=int((p/'pid').read_text()); cmd=pathlib.Path('/proc/'+str(pid)+'/cmdline').read_bytes()\n except (FileNotFoundError,ProcessLookupError): break\n if str(p/'server.py').encode() not in cmd: break\n time.sleep(.1)\nelse: sys.exit(1)\nshutil.rmtree(p)\n")} ${shq(dir)}`);
|
|
361
|
+
};
|
|
362
|
+
try {
|
|
363
|
+
await command(`mkdir -m 700 ${shq(dir)}`);
|
|
364
|
+
await write(`${dir}/snapshot.json`, '{"generation":0,"routes":{}}');
|
|
365
|
+
await write(`${dir}/server.py`, SERVER);
|
|
366
|
+
await command(`setsid -f python3 ${shq(`${dir}/server.py`)} ${shq(dir)} ${port} ${shq(nonce)} ${shq(RECEIVING_INBOX_CSP)} < /dev/null > /dev/null 2>&1`);
|
|
367
|
+
const ready = await command(`python3 -c ${shq("import sys,time,urllib.request\nend=time.monotonic()+float(sys.argv[3])\nwhile time.monotonic()<end:\n try:\n response=urllib.request.urlopen(sys.argv[1],timeout=.5)\n if response.read(128).decode()==sys.argv[2]: sys.exit(0)\n except Exception: pass\n time.sleep(.1)\nsys.exit(1)\n")} ${shq(`http://127.0.0.1:${port}/health`)} ${shq(nonce)} ${Math.max(0.1, timeout / 1000 - 0.2)}`);
|
|
368
|
+
void ready;
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
await stop().catch(() => undefined);
|
|
372
|
+
throw new Error("Receiving inbox could not start.");
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
url,
|
|
376
|
+
publish(files) {
|
|
377
|
+
if (stopped)
|
|
378
|
+
return Promise.reject(new Error("Receiving inbox is stopped."));
|
|
379
|
+
const routes = Object.create(null);
|
|
380
|
+
for (const file of files) {
|
|
381
|
+
if (!ROUTE.test(file.path) || routes[`/${file.path}`] || !["text/html; charset=utf-8", "application/json; charset=utf-8"].includes(file.contentType) || typeof file.body !== "string")
|
|
382
|
+
return Promise.reject(new Error("Invalid receiving inbox snapshot."));
|
|
383
|
+
routes[`/${file.path}`] = { ...file };
|
|
384
|
+
}
|
|
385
|
+
const snapshot = JSON.stringify({ generation: ++generation, routes });
|
|
386
|
+
if (files.length > 305 || Buffer.byteLength(snapshot) > MAX_SNAPSHOT_BYTES)
|
|
387
|
+
return Promise.reject(new Error("Receiving inbox snapshot exceeds limits."));
|
|
388
|
+
const next = tail.then(async () => {
|
|
389
|
+
if (stopped)
|
|
390
|
+
throw new Error("Receiving inbox is stopped.");
|
|
391
|
+
const temporary = `${dir}/snapshot-${randomUUID()}.json`;
|
|
392
|
+
try {
|
|
393
|
+
await write(temporary, snapshot);
|
|
394
|
+
if (stopped)
|
|
395
|
+
throw new Error("Receiving inbox is stopped.");
|
|
396
|
+
// Monotonic generations also reject an SDK operation that completes after its caller's
|
|
397
|
+
// timeout. The desktop-side lock covers read/compare/rename; transport timeouts do not.
|
|
398
|
+
await command(`python3 -c ${shq("import fcntl,json,os,pathlib,sys,urllib.request\np=pathlib.Path(sys.argv[1]); pending=pathlib.Path(sys.argv[2])\nwith (p/'publish.lock').open('a') as lock:\n fcntl.flock(lock,fcntl.LOCK_EX)\n if (p/'stop').exists(): sys.exit(1)\n with pending.open() as f: new=json.load(f)\n with (p/'snapshot.json').open() as f: old=json.load(f)\n if new['generation']<=old['generation']: sys.exit(1)\n os.replace(pending,p/'snapshot.json')\nwith urllib.request.urlopen(sys.argv[3],timeout=2) as response:\n if response.read(128).decode()!=sys.argv[4]: sys.exit(1)\n")} ${shq(dir)} ${shq(temporary)} ${shq(`http://127.0.0.1:${port}/health`)} ${shq(nonce)}`);
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
await command(`rm -f ${shq(temporary)}`).catch(() => undefined);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
tail = next.catch(() => undefined);
|
|
405
|
+
return next;
|
|
406
|
+
},
|
|
407
|
+
stop
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=comms-receiving-inbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comms-receiving-inbox.js","sourceRoot":"","sources":["../src/comms-receiving-inbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LabConfig } from "./lab-config.js";
|
|
2
|
+
import type { PreparedRunArtifactPaths } from "./run-paths.js";
|
|
3
|
+
/** Host-only resolution. The returned credential must never cross a desktop or UI boundary. */
|
|
4
|
+
export declare function resolveReceivingConnection(cwd: string, name: string, env: NodeJS.ProcessEnv): Promise<{
|
|
5
|
+
connection: import("./comms-connections.js").CommsConnection;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
adapter: import("./comms-receiving-types.js").ReceivingAdapter;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function prepareReceivingRun(args: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
config: LabConfig;
|
|
13
|
+
env: NodeJS.ProcessEnv;
|
|
14
|
+
participants: string[];
|
|
15
|
+
runPaths: PreparedRunArtifactPaths;
|
|
16
|
+
registerSecrets(values: string[]): void;
|
|
17
|
+
}): Promise<import("./comms-receiving.js").CommsReceivingRun | undefined>;
|
|
18
|
+
/** The restriction survives restarts and screenshot redaction; it is not a claim of local processing. */
|
|
19
|
+
export declare function receivingPublication(config: LabConfig, dryRun: boolean): {
|
|
20
|
+
publication: {
|
|
21
|
+
restrictions: ["real-communications"];
|
|
22
|
+
};
|
|
23
|
+
} | {
|
|
24
|
+
publication?: never;
|
|
25
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { registerTransientCommsSecrets } from "./run-narration-secrets.js";
|
|
2
|
+
import { readCommsConnections } from "./comms-connections.js";
|
|
3
|
+
import { discoverProviderKeys } from "./key-resolution.js";
|
|
4
|
+
import { createAgentMailReceiver } from "./comms-agentmail.js";
|
|
5
|
+
import { startCommsReceiving } from "./comms-receiving.js";
|
|
6
|
+
import { renderReceivingInbox } from "./comms-receiving-inbox.js";
|
|
7
|
+
import { writeContainedOutputFile } from "./selected-output-paths.js";
|
|
8
|
+
/** Host-only resolution. The returned credential must never cross a desktop or UI boundary. */
|
|
9
|
+
export async function resolveReceivingConnection(cwd, name, env) {
|
|
10
|
+
const connection = (await readCommsConnections(cwd)).connections[name];
|
|
11
|
+
if (!connection)
|
|
12
|
+
throw new Error("Email connection is missing. Open Connections in the TUI or run humanish comms connections add.");
|
|
13
|
+
const resolved = { ...env };
|
|
14
|
+
await discoverProviderKeys({ cwd, env: resolved, announce: () => { } });
|
|
15
|
+
const apiKey = resolved[connection.apiKeyEnv]?.trim();
|
|
16
|
+
if (!apiKey)
|
|
17
|
+
throw new Error("Email connection credential is missing. Add it in Connections, or provide the configured key with --env-file.");
|
|
18
|
+
return { connection, apiKey, adapter: createAgentMailReceiver({ apiKey }) };
|
|
19
|
+
}
|
|
20
|
+
export async function prepareReceivingRun(args) {
|
|
21
|
+
const email = args.config.comms?.email;
|
|
22
|
+
if (email?.kind !== "real")
|
|
23
|
+
return undefined;
|
|
24
|
+
const resolved = await resolveReceivingConnection(args.cwd, email.connection, args.env);
|
|
25
|
+
const registerSecrets = (values) => { registerTransientCommsSecrets(values); args.registerSecrets(values); };
|
|
26
|
+
registerSecrets([resolved.apiKey]);
|
|
27
|
+
if ((args.config.subject.env ?? []).some(name => name === resolved.connection.apiKeyEnv || args.env[name]?.includes(resolved.apiKey))
|
|
28
|
+
|| Object.entries(args.config.subject.envValues ?? {}).some(([name, value]) => name === resolved.connection.apiKeyEnv || String(value).includes(resolved.apiKey))) {
|
|
29
|
+
throw new Error("The inbox management credential cannot be forwarded through subject.env or subject.envValues. Use a separate target-app sending credential.");
|
|
30
|
+
}
|
|
31
|
+
return startCommsReceiving({
|
|
32
|
+
cwd: args.cwd, runId: args.runId, connectionName: email.connection,
|
|
33
|
+
apiKeyEnv: resolved.connection.apiKeyEnv, adapter: resolved.adapter,
|
|
34
|
+
participants: args.participants, registerSecrets,
|
|
35
|
+
render: renderReceivingInbox,
|
|
36
|
+
writeEvidence: evidence => writeContainedOutputFile(args.runPaths, "comms/receiving.json", `${JSON.stringify(evidence, null, 2)}\n`, "utf8")
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** The restriction survives restarts and screenshot redaction; it is not a claim of local processing. */
|
|
40
|
+
export function receivingPublication(config, dryRun) {
|
|
41
|
+
return !dryRun && config.comms?.email?.kind === "real"
|
|
42
|
+
? { publication: { restrictions: ["real-communications"] } }
|
|
43
|
+
: {};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=comms-receiving-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comms-receiving-runtime.js","sourceRoot":"","sources":["../src/comms-receiving-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/** Internal receiving-only contract. Provider identifiers and content are host/runtime-only. */
|
|
2
|
+
import type { CommsInlineImage } from "./comms-types.js";
|
|
3
|
+
export interface ReceivingContext {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ReceivingIdentity {
|
|
8
|
+
provider: "agentmail";
|
|
9
|
+
accountId: string;
|
|
10
|
+
scopeType: "organization" | "pod" | "inbox";
|
|
11
|
+
scopeId: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ReceivingLease {
|
|
14
|
+
resourceId: string;
|
|
15
|
+
address: string;
|
|
16
|
+
clientId: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ReceivedEmail {
|
|
19
|
+
channel: "email";
|
|
20
|
+
providerMessageId: string;
|
|
21
|
+
providerTimestamp?: string;
|
|
22
|
+
from: string;
|
|
23
|
+
subject?: string;
|
|
24
|
+
text: string;
|
|
25
|
+
html?: string;
|
|
26
|
+
inlineImages: CommsInlineImage[];
|
|
27
|
+
limitations: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface ReceivingBatch {
|
|
30
|
+
messages: ReceivedEmail[];
|
|
31
|
+
complete: boolean;
|
|
32
|
+
/** Stable safe codes only, never provider error bodies or identifiers. */
|
|
33
|
+
limitations: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface ReceivingAdapter {
|
|
36
|
+
readonly provider: "agentmail";
|
|
37
|
+
authenticate(context?: ReceivingContext): Promise<ReceivingIdentity>;
|
|
38
|
+
acquire(clientId: string, context?: ReceivingContext): Promise<ReceivingLease>;
|
|
39
|
+
read(lease: ReceivingLease, context?: ReceivingContext): Promise<ReceivingBatch>;
|
|
40
|
+
release(lease: ReceivingLease, context?: ReceivingContext): Promise<{
|
|
41
|
+
status: "absent" | "deleting";
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
/** Content provided to a single participant's desktop; no provider identifiers. */
|
|
45
|
+
export interface ParticipantEmail extends Omit<ReceivedEmail, "providerMessageId"> {
|
|
46
|
+
id: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ReceivingSurfaceFile {
|
|
49
|
+
path: string;
|
|
50
|
+
body: string;
|
|
51
|
+
contentType: "text/html; charset=utf-8" | "application/json; charset=utf-8";
|
|
52
|
+
}
|
|
53
|
+
export interface RenderedReceivingInbox {
|
|
54
|
+
files: ReceivingSurfaceFile[];
|
|
55
|
+
blockedAssetCount: number;
|
|
56
|
+
blockedLinkCount: number;
|
|
57
|
+
/** Register before publishing; never persist this array. */
|
|
58
|
+
secrets: string[];
|
|
59
|
+
linkCount: number;
|
|
60
|
+
codeCount: number;
|
|
61
|
+
}
|
|
62
|
+
export interface ReceivingSurface {
|
|
63
|
+
url: string;
|
|
64
|
+
publish(files: ReceivingSurfaceFile[]): Promise<void>;
|
|
65
|
+
stop(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export declare const COMMS_RECEIVING_SCHEMA = "humanish.comms-receiving.v2";
|
|
68
|
+
export interface ReceivingMessageEvidence {
|
|
69
|
+
id: string;
|
|
70
|
+
firstObservedAt: string;
|
|
71
|
+
providerTimestamp?: string;
|
|
72
|
+
publishedAt?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ReceivingParticipantEvidence {
|
|
75
|
+
participantId: string;
|
|
76
|
+
leaseId: string;
|
|
77
|
+
acquisition: "pending" | "active" | "failed";
|
|
78
|
+
cleanup: "pending" | "absent" | "deleting" | "unresolved";
|
|
79
|
+
observed: number;
|
|
80
|
+
published: number;
|
|
81
|
+
linkCount: number;
|
|
82
|
+
codeCount: number;
|
|
83
|
+
blockedAssetCount: number;
|
|
84
|
+
blockedLinkCount: number;
|
|
85
|
+
messages: ReceivingMessageEvidence[];
|
|
86
|
+
limitations: string[];
|
|
87
|
+
}
|
|
88
|
+
export interface CommsReceivingEvidence {
|
|
89
|
+
schema: typeof COMMS_RECEIVING_SCHEMA;
|
|
90
|
+
channel: "email";
|
|
91
|
+
provider: "agentmail";
|
|
92
|
+
publication: "restricted-real-communications";
|
|
93
|
+
state: "acquiring" | "running" | "finished";
|
|
94
|
+
participants: ReceivingParticipantEvidence[];
|
|
95
|
+
limitations: string[];
|
|
96
|
+
/** Inbox links are constrained; the rest of the browser is not network-confined. */
|
|
97
|
+
browserConfinement: "mail-surface-only";
|
|
98
|
+
}
|