humanish 0.26.0 → 0.28.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.
@@ -0,0 +1,60 @@
1
+ import type { CommsMessage } from "./comms-types.js";
2
+ /** [fromOrigin, toOrigin] pairs: rewrite a link/href whose origin is the app's in-sandbox origin into a
3
+ * lane-reachable origin, so the persona's browser can actually follow the verify link when it clicks. */
4
+ export type OriginMap = Array<[string, string]>;
5
+ export interface InboxRenderOptions {
6
+ originMap?: OriginMap;
7
+ }
8
+ /** One served file: a route path (no leading slash) + its body + content type. The host writes each to
9
+ * `<servedDir>/<path>`; the catch serves `<servedDir>/<pathname>` verbatim. */
10
+ export interface InboxSurfaceFile {
11
+ path: string;
12
+ contentType: "text/html; charset=utf-8" | "application/json; charset=utf-8";
13
+ body: string;
14
+ }
15
+ /** Rewrite a URL's origin per the map (origins are exact strings the harness minted). Leaves
16
+ * non-matching URLs — including sibling origins sharing a prefix — untouched. */
17
+ export declare function rewriteOrigin(url: string, originMap?: OriginMap): string;
18
+ /**
19
+ * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes ITS OWN
20
+ * origin into the verify links it emails (usually its loopback serve origin); the persona reaches the
21
+ * app at a possibly-different origin — the same loopback on the CUA route (identity, a no-op), the
22
+ * harness-minted getHost URL on the shared-world route (where the rewrite is REQUIRED). Emits the serve
23
+ * origin plus its loopback aliases at the serve port (127.0.0.1 / localhost / 0.0.0.0) so an app that
24
+ * stamps `localhost` still rewrites, and — because the harness cannot infer an absolute PUBLIC base URL
25
+ * an app was configured with — an operator-declared `linkOrigin` escape hatch, matched first. Origins
26
+ * only (no trailing slash, no path), so replaceOriginBoundary matches on a URL boundary.
27
+ */
28
+ export declare function buildOriginMap(args: {
29
+ internalServeUrl?: string | undefined;
30
+ reachableBaseUrl?: string | undefined;
31
+ linkOrigin?: string | undefined;
32
+ }): OriginMap;
33
+ /** Best-guess primary call-to-action: the first verify/confirm-looking link, else the first link. */
34
+ export declare function pickVerifyUrl(links: string[]): string | undefined;
35
+ /** The surface CSP — the browser-enforced, load-bearing protection against the app-authored email running
36
+ * script or hijacking navigation on the surface page. `script-src 'none'` blocks inline handlers,
37
+ * `javascript:` URLs, and any injected <script>; `object-src`/`frame-src 'none'` block plugins/frames;
38
+ * `base-uri 'none'` blocks <base> reroot. Images/styles stay permissive so the real email still renders.
39
+ * Set BOTH as a page() meta (covers direct render use) and as a catch response header (covers serving). */
40
+ export declare const INBOX_SURFACE_CSP = "default-src 'self'; script-src 'none'; object-src 'none'; base-uri 'none'; frame-src 'none'; img-src * data:; style-src 'unsafe-inline'; font-src * data:";
41
+ /** The inbox LIST page (semantic table; each row links to its message). */
42
+ export declare function renderInboxList(messages: CommsMessage[]): string;
43
+ /** One message, DEFAULT view: the app's real captured email in a minimal high-contrast shell. */
44
+ export declare function renderInboxMessage(message: CommsMessage, options?: InboxRenderOptions): string;
45
+ /** One message, SYNTHESIZED view: a guaranteed-legible reading pane (big verify button + big OTP). The
46
+ * reliability fallback when the app's real email is a vision minefield; opt-in, never the default. */
47
+ export declare function renderInboxMessageSynth(message: CommsMessage, options?: InboxRenderOptions): string;
48
+ /** JSON projection of one message (the programmatic/CLI actor surface + the reliability backstop). Raw
49
+ * runtime values (served in-sandbox only); links/verifyUrl origin-rewritten so a consumer can follow. */
50
+ export declare function inboxMessageJson(message: CommsMessage, options?: InboxRenderOptions): Record<string, unknown>;
51
+ /**
52
+ * Build every served file for the inbox surface from the normalized messages. The host writes each to
53
+ * `<servedDir>/<path>`; the in-sandbox catch serves `<servedDir>/<pathname>`, falling back to
54
+ * `<pathname>/index` (so a URL like `/inbox` maps to the `inbox/index` file while `/inbox/{id}` stays a
55
+ * directory) — standard web-server index semantics that avoid a file-vs-directory path collision. The
56
+ * URLs the persona actually sees stay clean (`/inbox`, `/inbox/{id}`, `/inbox/{id}/synth`). Content type
57
+ * is inferred from the `/api/` prefix. Message ids are `comms-NNNN` (never `index`/`latest`), so leaf
58
+ * filenames never collide with an id.
59
+ */
60
+ export declare function buildInboxSurface(messages: CommsMessage[], options?: InboxRenderOptions): InboxSurfaceFile[];
@@ -0,0 +1,290 @@
1
+ // The persona-facing INBOX SURFACE (#297, slice B): a minimal, dependency-free, high-contrast set of
2
+ // pages a computer-use persona opens to read a captured verification email and click its link — the
3
+ // last step of the off-app comms funnel (redirect → capture → drain → evidence → SURFACE).
4
+ //
5
+ // Design (research-backed, 2026-08-04): agent/CUA benchmarks hand agents either NO ui or a DELIBERATELY
6
+ // simplified inbox (MiniWoB email-inbox); nobody hands a vision agent a chrome-heavy webmail client.
7
+ // Anthropic computer-use guidance: high contrast, single column, big hit-targets, minimal state. So the
8
+ // surface is minimal SEMANTIC HTML (native <a>/<table>, black-on-white, large targets) — NOT a component
9
+ // UI (shadcn/Base UI break both the no-build ethos AND vision-agent reliability). We render the app's
10
+ // REAL captured email by default (faithful to the app-under-test — an honest signal if its email is a
11
+ // legibility mess), with a synthesized clean view one path away, plus a JSON twin for programmatic
12
+ // actors. Route shape mirrors Mailpit/Inbucket (list, message, `latest` alias, JSON with pre-extracted
13
+ // links/otp).
14
+ //
15
+ // This module is PURE + typed + tested; it consumes the already-normalized CommsMessage[] the tested
16
+ // drain/route path produces (links + codes already extracted), and emits the route→content files the
17
+ // in-sandbox catch serves statically. Raw content here is runtime-only (rendered in-sandbox, served to
18
+ // the in-sandbox browser); it is never persisted — the persisted evidence stays digest-only.
19
+ const VERIFY_HINT = /verify|confirm|activate|validate|magic|token|account|sign[\s-]?up/i;
20
+ /** Replace `from` with `to` everywhere it ends on an ORIGIN BOUNDARY — end-of-string or one of the URL
21
+ * delimiters `/ ? # " ' <space> > \`. A bare prefix replace would mangle a sibling origin that shares
22
+ * `from` as a numeric prefix (`:30000` vs `:3000`) or a suffix-domain (`:3000.evil`); the boundary
23
+ * check prevents that. Works both for a single URL and inside a blob of email HTML. */
24
+ function replaceOriginBoundary(text, from, to) {
25
+ if (!from)
26
+ return text;
27
+ let out = "";
28
+ let i = 0;
29
+ for (;;) {
30
+ const idx = text.indexOf(from, i);
31
+ if (idx === -1)
32
+ return out + text.slice(i);
33
+ const after = text.charAt(idx + from.length); // "" past end-of-string
34
+ const atBoundary = after === "" || "/?#\"' >\\".includes(after);
35
+ out += text.slice(i, idx) + (atBoundary ? to : from);
36
+ i = idx + from.length;
37
+ }
38
+ }
39
+ /** Rewrite a URL's origin per the map (origins are exact strings the harness minted). Leaves
40
+ * non-matching URLs — including sibling origins sharing a prefix — untouched. */
41
+ export function rewriteOrigin(url, originMap = []) {
42
+ let out = url;
43
+ for (const [from, to] of originMap)
44
+ out = replaceOriginBoundary(out, from, to);
45
+ return out;
46
+ }
47
+ function safeOrigin(url) {
48
+ if (!url)
49
+ return undefined;
50
+ try {
51
+ return new URL(url).origin;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ function safePort(url) {
58
+ if (!url)
59
+ return undefined;
60
+ try {
61
+ return new URL(url).port || undefined;
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
67
+ /**
68
+ * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes ITS OWN
69
+ * origin into the verify links it emails (usually its loopback serve origin); the persona reaches the
70
+ * app at a possibly-different origin — the same loopback on the CUA route (identity, a no-op), the
71
+ * harness-minted getHost URL on the shared-world route (where the rewrite is REQUIRED). Emits the serve
72
+ * origin plus its loopback aliases at the serve port (127.0.0.1 / localhost / 0.0.0.0) so an app that
73
+ * stamps `localhost` still rewrites, and — because the harness cannot infer an absolute PUBLIC base URL
74
+ * an app was configured with — an operator-declared `linkOrigin` escape hatch, matched first. Origins
75
+ * only (no trailing slash, no path), so replaceOriginBoundary matches on a URL boundary.
76
+ */
77
+ export function buildOriginMap(args) {
78
+ const to = safeOrigin(args.reachableBaseUrl);
79
+ if (to === undefined)
80
+ return [];
81
+ const froms = [];
82
+ const declared = safeOrigin(args.linkOrigin);
83
+ if (declared !== undefined)
84
+ froms.push(declared); // operator-declared origin wins (matched first)
85
+ const serve = safeOrigin(args.internalServeUrl);
86
+ if (serve !== undefined) {
87
+ froms.push(serve);
88
+ const port = safePort(args.internalServeUrl);
89
+ if (port !== undefined)
90
+ for (const host of ["127.0.0.1", "localhost", "0.0.0.0"])
91
+ froms.push(`http://${host}:${port}`);
92
+ }
93
+ const seen = new Set();
94
+ const map = [];
95
+ for (const from of froms) {
96
+ if (from !== to && !seen.has(from)) {
97
+ seen.add(from);
98
+ map.push([from, to]);
99
+ }
100
+ }
101
+ return map;
102
+ }
103
+ /** Best-guess primary call-to-action: the first verify/confirm-looking link, else the first link. */
104
+ export function pickVerifyUrl(links) {
105
+ return links.find((link) => VERIFY_HINT.test(link)) ?? links[0];
106
+ }
107
+ function esc(value) {
108
+ return value
109
+ .replace(/&/g, "&amp;")
110
+ .replace(/</g, "&lt;")
111
+ .replace(/>/g, "&gt;")
112
+ .replace(/"/g, "&quot;");
113
+ }
114
+ /** Defense-in-depth stripping before rendering the app's real email HTML. The LOAD-BEARING protection is
115
+ * the page CSP (`script-src 'none'`, set both as a page() meta and as a catch response header), which is
116
+ * browser-enforced and neuters inline handlers, `javascript:` URLs, and injected <script> regardless of
117
+ * what this misses. This strip additionally removes elements CSP does not cover (a redirecting
118
+ * <meta http-equiv=refresh>, a nav-rerooting <base>) and the obvious active tags, so the two layers
119
+ * together stop the app-authored (untrusted) email from executing script or redirecting the surface. */
120
+ function neutralizeEmailHtml(html, originMap) {
121
+ let out = html
122
+ // paired dangerous/framing elements + their content
123
+ .replace(/<(script|style|iframe|object|embed|svg|math|template)\b[\s\S]*?<\/\1\s*>/gi, "")
124
+ // stray or void dangerous tags, incl. unclosed openers and redirect/reroot tags
125
+ .replace(/<\/?(script|style|iframe|object|embed|svg|math|base|meta|link|form)\b[^>]*>/gi, "")
126
+ // inline event handlers, whether preceded by whitespace OR a "/" attribute separator
127
+ .replace(/[\s/]on[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, " ")
128
+ // javascript:/vbscript: in nav/resource attrs — quoted OR unquoted (data: is left for inline images)
129
+ .replace(/(href|src|xlink:href|formaction|action)\s*=\s*(?:"(?:javascript|vbscript):[^"]*"|'(?:javascript|vbscript):[^']*'|(?:javascript|vbscript):[^\s>]*)/gi, '$1="#"');
130
+ // Rewrite the app's origin inside the (now-neutralized) HTML so its own links resolve to a reachable host.
131
+ for (const [from, to] of originMap)
132
+ out = replaceOriginBoundary(out, from, to);
133
+ return out;
134
+ }
135
+ const PAGE_CSS = "body{font:16px/1.55 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#111;background:#fff;margin:0}" +
136
+ "a{color:#0645ad}" +
137
+ ".bar{padding:10px 20px;background:#f4f4f4;border-bottom:1px solid #ccc;font-size:14px}" +
138
+ ".hdr{padding:16px 20px;border-bottom:3px solid #111}" +
139
+ ".hdr div{margin:2px 0}.hdr b{display:inline-block;min-width:72px;color:#444;font-weight:600}" +
140
+ ".body{padding:20px;max-width:820px}" +
141
+ "table{border-collapse:collapse;width:100%;max-width:900px}" +
142
+ "th,td{text-align:left;padding:12px 16px;border-bottom:1px solid #ddd;font-size:16px}" +
143
+ "th{border-bottom:2px solid #111}" +
144
+ "tr:hover{background:#f7f7f7}" +
145
+ ".verify{display:inline-block;padding:16px 32px;background:#111;color:#fff;text-decoration:none;font-size:18px;font-weight:600;border-radius:6px}" +
146
+ ".otp{font:32px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:8px;background:#f0f0f0;padding:12px 18px;display:inline-block;border-radius:6px}" +
147
+ ".muted{color:#666;font-size:13px}";
148
+ /** The surface CSP — the browser-enforced, load-bearing protection against the app-authored email running
149
+ * script or hijacking navigation on the surface page. `script-src 'none'` blocks inline handlers,
150
+ * `javascript:` URLs, and any injected <script>; `object-src`/`frame-src 'none'` block plugins/frames;
151
+ * `base-uri 'none'` blocks <base> reroot. Images/styles stay permissive so the real email still renders.
152
+ * Set BOTH as a page() meta (covers direct render use) and as a catch response header (covers serving). */
153
+ export const INBOX_SURFACE_CSP = "default-src 'self'; script-src 'none'; object-src 'none'; base-uri 'none'; frame-src 'none'; img-src * data:; style-src 'unsafe-inline'; font-src * data:";
154
+ function page(title, inner) {
155
+ return ("<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\">" +
156
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"" + INBOX_SURFACE_CSP + "\">" +
157
+ "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">" +
158
+ "<title>" + esc(title) + "</title><style>" + PAGE_CSS + "</style></head><body>" +
159
+ inner +
160
+ "</body></html>");
161
+ }
162
+ function fmtTime(ms) {
163
+ // Deterministic, dependency-free UTC stamp (avoids Date locale/timezone drift in tests + sandbox).
164
+ if (!Number.isFinite(ms) || ms <= 0)
165
+ return "";
166
+ const d = new Date(ms);
167
+ const p = (n) => String(n).padStart(2, "0");
168
+ return `${d.getUTCFullYear()}-${p(d.getUTCMonth() + 1)}-${p(d.getUTCDate())} ${p(d.getUTCHours())}:${p(d.getUTCMinutes())} UTC`;
169
+ }
170
+ function toValues(message) {
171
+ return message.to.map((address) => address.value);
172
+ }
173
+ /** The inbox LIST page (semantic table; each row links to its message). */
174
+ export function renderInboxList(messages) {
175
+ const rows = messages
176
+ .map((message) => "<tr><td><a href=\"/inbox/" + esc(message.id) + "\">" + esc(message.subject ?? "(no subject)") + "</a></td>" +
177
+ "<td>" + esc(message.from) + "</td>" +
178
+ "<td>" + esc(toValues(message).join(", ")) + "</td>" +
179
+ "<td class=\"muted\">" + esc(fmtTime(message.deliveredAt)) + "</td></tr>")
180
+ .join("");
181
+ const body = "<div class=\"bar\">Inbox — " + messages.length + " message" + (messages.length === 1 ? "" : "s") + "</div>" +
182
+ "<main class=\"body\"><table><thead><tr><th>Subject</th><th>From</th><th>To</th><th>Received</th></tr></thead>" +
183
+ "<tbody>" + (rows || "<tr><td colspan=\"4\" class=\"muted\">No messages yet.</td></tr>") + "</tbody></table></main>";
184
+ return page("Inbox", body);
185
+ }
186
+ /** One message, DEFAULT view: the app's real captured email in a minimal high-contrast shell. */
187
+ export function renderInboxMessage(message, options = {}) {
188
+ const originMap = options.originMap ?? [];
189
+ const realHtml = message.body ? neutralizeEmailHtml(message.body, originMap) : "";
190
+ const fallback = realHtml.trim().length > 0 ? realHtml : "<p class=\"muted\">(this message had no HTML body)</p>";
191
+ const body = "<div class=\"bar\"><a href=\"/inbox\">&larr; Inbox</a> &middot; <a href=\"/inbox/" + esc(message.id) + "/synth\">plain view</a></div>" +
192
+ "<div class=\"hdr\"><div><b>To</b> " + esc(toValues(message).join(", ")) + "</div>" +
193
+ "<div><b>From</b> " + esc(message.from) + "</div>" +
194
+ "<div><b>Subject</b> " + esc(message.subject ?? "(no subject)") + "</div></div>" +
195
+ "<main class=\"body\">" + fallback + "</main>";
196
+ return page(message.subject ?? "Message", body);
197
+ }
198
+ /** One message, SYNTHESIZED view: a guaranteed-legible reading pane (big verify button + big OTP). The
199
+ * reliability fallback when the app's real email is a vision minefield; opt-in, never the default. */
200
+ export function renderInboxMessageSynth(message, options = {}) {
201
+ const originMap = options.originMap ?? [];
202
+ const verify = pickVerifyUrl(message.links);
203
+ const verifyRewritten = verify ? rewriteOrigin(verify, originMap) : undefined;
204
+ const otp = message.codes[0];
205
+ const parts = [
206
+ "<div class=\"bar\"><a href=\"/inbox\">&larr; Inbox</a> &middot; <a href=\"/inbox/" + esc(message.id) + "\">real email</a></div>",
207
+ "<div class=\"hdr\"><div><b>From</b> " + esc(message.from) + "</div>" +
208
+ "<div><b>To</b> " + esc(toValues(message).join(", ")) + "</div>" +
209
+ "<div><b>Subject</b> " + esc(message.subject ?? "(no subject)") + "</div></div>",
210
+ "<main class=\"body\" style=\"text-align:center\">"
211
+ ];
212
+ if (verifyRewritten) {
213
+ parts.push("<p style=\"margin:32px 0\"><a class=\"verify\" href=\"" + esc(verifyRewritten) + "\">Verify email</a></p>");
214
+ }
215
+ if (otp) {
216
+ parts.push("<p>Your code:</p><p><span class=\"otp\">" + esc(otp) + "</span></p>");
217
+ }
218
+ if (!verifyRewritten && !otp) {
219
+ parts.push("<p class=\"muted\">No verification link or code was found in this message. Open the <a href=\"/inbox/" + esc(message.id) + "\">real email</a>.</p>");
220
+ }
221
+ parts.push("</main>");
222
+ return page(message.subject ?? "Message", parts.join(""));
223
+ }
224
+ /** JSON projection of one message (the programmatic/CLI actor surface + the reliability backstop). Raw
225
+ * runtime values (served in-sandbox only); links/verifyUrl origin-rewritten so a consumer can follow. */
226
+ export function inboxMessageJson(message, options = {}) {
227
+ const originMap = options.originMap ?? [];
228
+ const links = message.links.map((link) => rewriteOrigin(link, originMap));
229
+ const verify = pickVerifyUrl(links);
230
+ return {
231
+ id: message.id,
232
+ from: message.from,
233
+ to: toValues(message),
234
+ subject: message.subject ?? null,
235
+ receivedAt: message.deliveredAt,
236
+ text: message.body,
237
+ links,
238
+ ...(verify === undefined ? {} : { verifyUrl: verify }),
239
+ ...(message.codes[0] === undefined ? {} : { otp: message.codes[0] })
240
+ };
241
+ }
242
+ function inboxListJson(messages, options = {}) {
243
+ const originMap = options.originMap ?? [];
244
+ return messages.map((message) => {
245
+ const verify = pickVerifyUrl(message.links.map((link) => rewriteOrigin(link, originMap)));
246
+ return {
247
+ id: message.id,
248
+ from: message.from,
249
+ to: toValues(message),
250
+ subject: message.subject ?? null,
251
+ receivedAt: message.deliveredAt,
252
+ ...(verify === undefined ? {} : { verifyUrl: verify }),
253
+ ...(message.codes[0] === undefined ? {} : { otp: message.codes[0] })
254
+ };
255
+ });
256
+ }
257
+ /**
258
+ * Build every served file for the inbox surface from the normalized messages. The host writes each to
259
+ * `<servedDir>/<path>`; the in-sandbox catch serves `<servedDir>/<pathname>`, falling back to
260
+ * `<pathname>/index` (so a URL like `/inbox` maps to the `inbox/index` file while `/inbox/{id}` stays a
261
+ * directory) — standard web-server index semantics that avoid a file-vs-directory path collision. The
262
+ * URLs the persona actually sees stay clean (`/inbox`, `/inbox/{id}`, `/inbox/{id}/synth`). Content type
263
+ * is inferred from the `/api/` prefix. Message ids are `comms-NNNN` (never `index`/`latest`), so leaf
264
+ * filenames never collide with an id.
265
+ */
266
+ export function buildInboxSurface(messages, options = {}) {
267
+ const html = (path, body) => ({ path, contentType: "text/html; charset=utf-8", body });
268
+ const json = (path, value) => ({
269
+ path,
270
+ contentType: "application/json; charset=utf-8",
271
+ body: JSON.stringify(value, null, 2)
272
+ });
273
+ const files = [
274
+ html("inbox/index", renderInboxList(messages)),
275
+ json("api/inbox/index", inboxListJson(messages, options))
276
+ ];
277
+ const latest = messages[messages.length - 1];
278
+ for (const message of messages) {
279
+ files.push(html("inbox/" + message.id + "/index", renderInboxMessage(message, options)));
280
+ files.push(html("inbox/" + message.id + "/synth", renderInboxMessageSynth(message, options)));
281
+ files.push(json("api/inbox/" + message.id, inboxMessageJson(message, options)));
282
+ }
283
+ if (latest) {
284
+ files.push(html("inbox/latest/index", renderInboxMessage(latest, options)));
285
+ files.push(html("inbox/latest/synth", renderInboxMessageSynth(latest, options)));
286
+ files.push(json("api/inbox/latest", inboxMessageJson(latest, options)));
287
+ }
288
+ return files;
289
+ }
290
+ //# sourceMappingURL=comms-inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comms-inbox.js","sourceRoot":"","sources":["../src/comms-inbox.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import type { CommsAddress, CommsChannel } from "./comms-types.js";
1
+ import type { CommsAddress, CommsChannel, CommsMessage } from "./comms-types.js";
2
2
  import { type CommsThreadArtifact } from "./comms-evidence.js";
3
+ import { type InboxRenderOptions } from "./comms-inbox.js";
3
4
  import { type EmailSendProfile } from "./comms-email-catch.js";
4
5
  import { type DetachedTimers } from "./e2b-detached.js";
5
6
  import type { E2BDesktopSandbox } from "./e2b-desktop-launch.js";
@@ -32,6 +33,9 @@ export interface DeployedCommsCatch {
32
33
  /** Inject THIS as the app's email-API base URL (e.g. RESEND_API_URL) — the sandbox's own loopback. */
33
34
  baseUrl: string;
34
35
  deliveriesPath: string;
36
+ /** In-sandbox dir the HOST renders the persona-facing inbox-surface files into (via writeInboxSurface);
37
+ * the catch serves them at /inbox and /api/inbox. */
38
+ surfaceDir: string;
35
39
  /** Whether the catch's /health returned OUR service marker within the readiness budget. Callers MUST
36
40
  * treat `ready === false` as fatal (do not inject baseUrl into a dead catch — the app's sends would
37
41
  * silently fail with nothing captured). */
@@ -92,3 +96,37 @@ export declare function collectCommsThread(args: {
92
96
  profiles?: EmailSendProfile[];
93
97
  requestTimeoutMs?: number;
94
98
  }): Promise<CommsThreadCollection>;
99
+ /**
100
+ * Render the persona-facing inbox surface (host-side, typed — see comms-inbox.ts) and write the files
101
+ * into the sandbox's served dir, so the catch serves a LIVE inbox the persona opens and clicks. Creates
102
+ * the nested route dirs first; overwrites idempotently, so call it whenever the message set changes
103
+ * (e.g. after a mid-run drain). Returns the number of files written. Raw content is written INTO the
104
+ * sandbox only (runtime-only, served to the in-sandbox browser); nothing here persists to the bundle.
105
+ */
106
+ export declare function writeInboxSurface(desktop: E2BDesktopSandbox, surfaceDir: string, messages: CommsMessage[], options?: InboxRenderOptions & {
107
+ requestTimeoutMs?: number;
108
+ }): Promise<number>;
109
+ /**
110
+ * One mid-run inbox-surface refresh cycle: drain the catch INCREMENTALLY from `cursor` into the
111
+ * persistent surface `channel`, poll the provisioned `inboxes`, and (re)render the surface so the
112
+ * persona sees new mail while the session is live. Returns the advanced cursor + whether it rendered.
113
+ *
114
+ * MUST use a channel dedicated to the surface — NOT the teardown evidence channel: the teardown
115
+ * collectCommsThread drains the same append-only NDJSON from cursor 0 into its OWN fresh channel, so
116
+ * feeding both an incremental and a cursor-0 drain into one channel would route every send twice
117
+ * (FakeInbox mints fresh ids each time, so dedup-by-id can't catch it) and double-count the evidence.
118
+ * Two channels + two cursors are independent readers of the same file — no evidence is lost or altered.
119
+ * Skips the (N-file) render when no new sends arrived this tick, so idle ticks are cheap.
120
+ */
121
+ export declare function refreshInboxSurface(args: {
122
+ desktop: E2BDesktopSandbox;
123
+ deployed: Pick<DeployedCommsCatch, "deliveriesPath" | "surfaceDir">;
124
+ channel: CommsChannel;
125
+ inboxes: CommsAddress[];
126
+ cursor: number;
127
+ originMap?: InboxRenderOptions["originMap"];
128
+ requestTimeoutMs?: number;
129
+ }): Promise<{
130
+ cursor: number;
131
+ rendered: boolean;
132
+ }>;
@@ -7,6 +7,7 @@
7
7
  // parse them and route into the CommsChannel. A FIXED loopback port is chosen up front so the app's
8
8
  // injected base-URL env (`http://127.0.0.1:<port>`) is known before the sandbox is created.
9
9
  import { buildCommsThreadArtifact } from "./comms-evidence.js";
10
+ import { buildInboxSurface } from "./comms-inbox.js";
10
11
  import { DEFAULT_EMAIL_PROFILES } from "./comms-email-catch.js";
11
12
  import { startDetachedProcess } from "./e2b-detached.js";
12
13
  /** The default in-sandbox loopback port for the catch. Fixed (not ephemeral) so the injected base-URL
@@ -27,15 +28,31 @@ function shq(value) {
27
28
  */
28
29
  export const SANDBOX_CATCH_SCRIPT = [
29
30
  'import { createServer } from "node:http";',
30
- 'import { appendFileSync, mkdirSync } from "node:fs";',
31
+ 'import { appendFileSync, mkdirSync, readFileSync } from "node:fs";',
31
32
  'import { dirname } from "node:path";',
32
33
  'const port = Number(process.argv[2] || 8025);',
33
34
  'const outFile = process.argv[3] || "/tmp/humanish-comms/deliveries.ndjson";',
35
+ // argv[4] is the dir the HOST renders the inbox-surface files into (host-side typed rendering; this
36
+ // server only serves them statically), defaulting to a `surface` sibling of the deliveries file.
37
+ 'const servedDir = process.argv[4] || (dirname(outFile) + "/surface");',
34
38
  'try { mkdirSync(dirname(outFile), { recursive: true }); } catch {}',
35
39
  'const MAX = 5 * 1024 * 1024;',
36
40
  'createServer((req, res) => {',
37
41
  ' const path = (req.url || "/").split("?")[0];',
38
42
  ' if (req.method === "GET" && (path === "/" || path === "/health")) { res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify({ ok: true, service: "humanish-comms-catch" })); return; }',
43
+ ' if (req.method === "GET" && (path === "/inbox" || path.indexOf("/inbox/") === 0 || path === "/api/inbox" || path.indexOf("/api/inbox/") === 0)) {',
44
+ ' let rel; try { rel = decodeURIComponent(path); } catch { res.writeHead(400); res.end(); return; }',
45
+ ' if (rel.indexOf("..") !== -1 || rel.indexOf("\\0") !== -1) { res.writeHead(400); res.end(); return; }',
46
+ ' let data = null;',
47
+ ' try { data = readFileSync(servedDir + rel); } catch { try { data = readFileSync(servedDir + rel + "/index"); } catch { data = null; } }',
48
+ ' if (data === null) { res.writeHead(404, { "content-type": "text/html; charset=utf-8" }); res.end("<p>message not found</p>"); return; }',
49
+ ' const isApi = rel.indexOf("/api/") === 0;',
50
+ ' const headers = { "content-type": isApi ? "application/json; charset=utf-8" : "text/html; charset=utf-8" };',
51
+ // The load-bearing XSS protection: a browser-enforced CSP forbidding all script on the surface page,
52
+ // so the app-authored (untrusted) email HTML we render cannot run script or reroot navigation.
53
+ ' if (!isApi) { headers["content-security-policy"] = "default-src \'self\'; script-src \'none\'; object-src \'none\'; base-uri \'none\'; frame-src \'none\'; img-src * data:; style-src \'unsafe-inline\'; font-src * data:"; }',
54
+ ' res.writeHead(200, headers); res.end(data); return;',
55
+ ' }',
39
56
  ' if (req.method !== "POST") { res.writeHead(404, { "content-type": "application/json" }); res.end(JSON.stringify({ error: "not found" })); return; }',
40
57
  ' let size = 0; const chunks = [];',
41
58
  ' req.on("data", (c) => { size += c.length; if (size > MAX) { req.destroy(); } else { chunks.push(c); } });',
@@ -83,11 +100,12 @@ export async function deployCommsCatch(desktop, options = {}) {
83
100
  const requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
84
101
  const scriptPath = `${dir}/catch.mjs`;
85
102
  const deliveriesPath = `${dir}/deliveries.ndjson`;
86
- await desktop.commands.run(`mkdir -p ${shq(dir)}`, { requestTimeoutMs });
103
+ const surfaceDir = `${dir}/surface`;
104
+ await desktop.commands.run(`mkdir -p ${shq(dir)} ${shq(surfaceDir)}`, { requestTimeoutMs });
87
105
  await desktop.files.write(scriptPath, SANDBOX_CATCH_SCRIPT);
88
106
  await startDetachedProcess(desktop, {
89
107
  name,
90
- command: `node ${shq(scriptPath)} ${port} ${shq(deliveriesPath)}`,
108
+ command: `node ${shq(scriptPath)} ${port} ${shq(deliveriesPath)} ${shq(surfaceDir)}`,
91
109
  requestTimeoutMs
92
110
  });
93
111
  const ready = await catchHealthy(desktop, port, {
@@ -95,7 +113,7 @@ export async function deployCommsCatch(desktop, options = {}) {
95
113
  requestTimeoutMs,
96
114
  ...(options.timers ?? {})
97
115
  });
98
- return { port, baseUrl: `http://127.0.0.1:${port}`, deliveriesPath, ready };
116
+ return { port, baseUrl: `http://127.0.0.1:${port}`, deliveriesPath, surfaceDir, ready };
99
117
  }
100
118
  /**
101
119
  * Drain new captured sends from the in-sandbox NDJSON since `cursor` (a line count). Returns the fresh
@@ -186,4 +204,62 @@ export async function collectCommsThread(args) {
186
204
  messages.sort((a, b) => a.deliveredAt - b.deliveredAt || a.id.localeCompare(b.id));
187
205
  return { artifact: buildCommsThreadArtifact(messages), captured: sends.length, matched: messages.length };
188
206
  }
207
+ /**
208
+ * Render the persona-facing inbox surface (host-side, typed — see comms-inbox.ts) and write the files
209
+ * into the sandbox's served dir, so the catch serves a LIVE inbox the persona opens and clicks. Creates
210
+ * the nested route dirs first; overwrites idempotently, so call it whenever the message set changes
211
+ * (e.g. after a mid-run drain). Returns the number of files written. Raw content is written INTO the
212
+ * sandbox only (runtime-only, served to the in-sandbox browser); nothing here persists to the bundle.
213
+ */
214
+ export async function writeInboxSurface(desktop, surfaceDir, messages, options = {}) {
215
+ const files = buildInboxSurface(messages, options);
216
+ // Create the union of parent dirs (inbox/<id>/synth etc.) in one mkdir before writing.
217
+ const dirs = new Set([surfaceDir]);
218
+ for (const file of files) {
219
+ const slash = file.path.lastIndexOf("/");
220
+ if (slash > 0)
221
+ dirs.add(`${surfaceDir}/${file.path.slice(0, slash)}`);
222
+ }
223
+ await desktop.commands.run(`mkdir -p ${[...dirs].map(shq).join(" ")}`, { requestTimeoutMs: options.requestTimeoutMs ?? 30_000 });
224
+ for (const file of files) {
225
+ await desktop.files.write(`${surfaceDir}/${file.path}`, file.body);
226
+ }
227
+ return files.length;
228
+ }
229
+ /**
230
+ * One mid-run inbox-surface refresh cycle: drain the catch INCREMENTALLY from `cursor` into the
231
+ * persistent surface `channel`, poll the provisioned `inboxes`, and (re)render the surface so the
232
+ * persona sees new mail while the session is live. Returns the advanced cursor + whether it rendered.
233
+ *
234
+ * MUST use a channel dedicated to the surface — NOT the teardown evidence channel: the teardown
235
+ * collectCommsThread drains the same append-only NDJSON from cursor 0 into its OWN fresh channel, so
236
+ * feeding both an incremental and a cursor-0 drain into one channel would route every send twice
237
+ * (FakeInbox mints fresh ids each time, so dedup-by-id can't catch it) and double-count the evidence.
238
+ * Two channels + two cursors are independent readers of the same file — no evidence is lost or altered.
239
+ * Skips the (N-file) render when no new sends arrived this tick, so idle ticks are cheap.
240
+ */
241
+ export async function refreshInboxSurface(args) {
242
+ const { sends, cursor } = await drainCommsCatch(args.desktop, args.deployed, args.cursor, args.requestTimeoutMs);
243
+ if (sends.length === 0)
244
+ return { cursor, rendered: false };
245
+ await routeCapturedSends(sends, args.channel);
246
+ const seen = new Set();
247
+ const messages = [];
248
+ for (const inbox of args.inboxes) {
249
+ for (const message of await args.channel.poll(inbox, 0)) {
250
+ if (seen.has(message.id))
251
+ continue;
252
+ seen.add(message.id);
253
+ messages.push(message);
254
+ }
255
+ }
256
+ if (messages.length === 0)
257
+ return { cursor, rendered: false };
258
+ messages.sort((a, b) => a.deliveredAt - b.deliveredAt || a.id.localeCompare(b.id));
259
+ await writeInboxSurface(args.desktop, args.deployed.surfaceDir, messages, {
260
+ ...(args.originMap === undefined ? {} : { originMap: args.originMap }),
261
+ ...(args.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: args.requestTimeoutMs })
262
+ });
263
+ return { cursor, rendered: true };
264
+ }
189
265
  //# sourceMappingURL=comms-sandbox-catch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"comms-sandbox-catch.js","sourceRoot":"","sources":["../src/comms-sandbox-catch.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,wGAAwG;AACxG,uGAAuG;AACvG,qGAAqG;AACrG,qGAAqG;AACrG,qGAAqG;AACrG,oGAAoG;AACpG,4FAA4F;AAG5F,OAAO,EAAE,wBAAwB,EAA4B,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAyB,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAuB,MAAM,mBAAmB,CAAC;AAG9E;;8EAE8E;AAC9E,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAC/C,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,iDAAiD;AACjD,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,2CAA2C;IAC3C,sDAAsD;IACtD,sCAAsC;IACtC,+CAA+C;IAC/C,6EAA6E;IAC7E,oEAAoE;IACpE,8BAA8B;IAC9B,8BAA8B;IAC9B,gDAAgD;IAChD,qNAAqN;IACrN,uJAAuJ;IACvJ,oCAAoC;IACpC,6GAA6G;IAC7G,yBAAyB;IACzB,0DAA0D;IAC1D,sGAAsG;IACtG,sGAAsG;IACtG,8FAA8F;IAC9F,oJAAoJ;IACpJ,2GAA2G;IAC3G,OAAO;IACP,+EAA+E;IAC/E,+BAA+B;CAChC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AA0Bb;0GAC0G;AAC1G,KAAK,UAAU,YAAY,CACzB,OAA0B,EAC1B,IAAY,EACZ,OAAyE;IAEzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACzG,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IAC3C,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ;aAClC,GAAG,CAAC,yCAAyC,IAAI,6BAA6B,EAAE,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;aAC/H,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YAAE,OAAO,IAAI,CAAC;QACxE,IAAI,GAAG,EAAE,IAAI,QAAQ;YAAE,OAAO,KAAK,CAAC;QACpC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AASD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAA0B,EAC1B,UAAmC,EAAE;IAErC,kGAAkG;IAClG,sGAAsG;IACtG,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,0BAA0B,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,iBAAiB,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,aAAa,CAAC;IAC3C,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,MAAM,CAAC;IAC5D,MAAM,UAAU,GAAG,GAAG,GAAG,YAAY,CAAC;IACtC,MAAM,cAAc,GAAG,GAAG,GAAG,oBAAoB,CAAC;IAElD,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACzE,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;IAC5D,MAAM,oBAAoB,CAAC,OAAO,EAAE;QAClC,IAAI;QACJ,OAAO,EAAE,QAAQ,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,cAAc,CAAC,EAAE;QACjE,gBAAgB;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE;QAC9C,SAAS,EAAE,OAAO,CAAC,cAAc,IAAI,MAAM;QAC3C,gBAAgB;QAChB,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;KAC1B,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,oBAAoB,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA0B,EAC1B,QAAoD,EACpD,MAAM,GAAG,CAAC,EACV,gBAAgB,GAAG,MAAM;IAEzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,sBAAsB,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC3H,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IACnC,IAAI,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACxE,oGAAoG;IACpG,oGAAoG;IACpG,sGAAsG;IACtG,wEAAwE;IACxE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;YAC3D,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvG,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAwB,EACxB,OAAqB,EACrB,WAA+B,sBAAsB;IAErD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;QACrG,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QACpC,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAC1D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;gBACxC,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,EAAE,EAAE,UAAU,CAAC,EAAE;gBACjB,GAAG,CAAC,UAAU,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC;gBAC5E,IAAI,EAAE,UAAU,CAAC,IAAI;aACtB,CAAC,CAAC;YACH,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAcD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAQxC;IACC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC/F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC3D,MAAM,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,KAAK,MAAM,OAAO,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;YACxD,yFAAyF;YACzF,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAAE,SAAS;YACnC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACzE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,OAAO,EAAE,QAAQ,EAAE,wBAAwB,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;AAC5G,CAAC"}
1
+ {"version":3,"file":"comms-sandbox-catch.js","sourceRoot":"","sources":["../src/comms-sandbox-catch.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import type { CuaExecutor, CuaLoopResult, CuaProvider } from "./computer-use.js"
6
6
  import { type E2BDesktopModule, type E2BDesktopSandbox } from "./e2b-desktop-launch.js";
7
7
  import { type DetachedTimers } from "./e2b-detached.js";
8
8
  import { type DevicePreset } from "./device-presets.js";
9
- import { type LabActorLane, type LabConfig, type LabDesktopBrowser, type LabSubjectServe, type LabSubjectState } from "./lab-config.js";
9
+ import { type LabActorLane, type LabCommsEmail, type LabConfig, type LabDesktopBrowser, type LabSubjectServe, type LabSubjectState } from "./lab-config.js";
10
10
  import { renderObserver, type ObserverResult } from "./observer.js";
11
11
  import { type PreparedOutputDirectory } from "./selected-output-paths.js";
12
12
  import { type LocalTreeArchive } from "./source-archive.js";
@@ -376,6 +376,15 @@ export declare function composeLaneInstructions(args: {
376
376
  instructions: string;
377
377
  persona: ActorPersonaRef;
378
378
  };
379
+ /** Runtime-inject the persona inbox instruction into a lane's prompt (#297 slice B). The inbox URL is a
380
+ * runtime loopback/getHost address (not secret), so — mirroring the lobby-code runtime injection — this
381
+ * augments only the instructions the model receives; the authored prompt + its digest are unchanged.
382
+ * Returns a new spec (never mutates). Shared by the CUA + concurrent shared-world routes. */
383
+ export declare function withInboxMission(spec: CuaLaneSpec, inboxUrl: string): CuaLaneSpec;
384
+ /** True when a lane has a declared comms recipient WITH an address, so the drain can actually match the
385
+ * mail the persona will be told to read. Gates the inbox instruction to lanes that can receive mail —
386
+ * a lane told to check an inbox it can never receive into would just stall. */
387
+ export declare function laneHasInboxRecipient(commsEmail: LabCommsEmail, laneId: string): boolean;
379
388
  /**
380
389
  * The narrowest browser WINDOW Chrome/Chromium will render on the E2B desktop. Chrome refuses to
381
390
  * make its window narrower than this (~500 CSS px observed: a 414-wide X screen produced a 500-wide