humanish 0.29.0 → 0.30.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/dist/comms-sandbox-catch.d.ts +31 -16
- package/dist/comms-sandbox-catch.js +66 -27
- package/dist/comms-sandbox-catch.js.map +1 -1
- package/dist/concurrent-shared-world-lab.js +80 -5
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.js +11 -17
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/lab-config.js +3 -0
- package/dist/lab-config.js.map +1 -1
- package/docs/contracts/schemas.md +1 -1
- package/docs/goals/current.md +1 -1
- package/docs/ramp/README.md +1 -1
- package/package.json +1 -1
|
@@ -17,13 +17,20 @@ export declare const DEFAULT_SANDBOX_CATCH_PORT = 8025;
|
|
|
17
17
|
* and returns a plausible provider success — all normalization/profile parsing happens host-side on the
|
|
18
18
|
* drained lines, so the typed, tested profiles stay in one place. It also serves the host-rendered inbox
|
|
19
19
|
* surface statically at /inbox + /api/inbox (with a script-forbidding CSP). argv: <port> <deliveriesFile>
|
|
20
|
-
* [
|
|
21
|
-
*
|
|
20
|
+
* <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback) — the app under test reaches
|
|
21
|
+
* it in-sandbox, nothing on the internet can inject a fake send. When an [inboxPort] is given (the
|
|
22
|
+
* shared-world route, where the persona lives in a DIFFERENT sandbox), it ALSO starts a READ-ONLY inbox
|
|
23
|
+
* listener on 0.0.0.0:<inboxPort> so getHost can proxy the persona's inbox reads to it; that listener
|
|
24
|
+
* serves GET only (POST → 405). The CUA same-sandbox route omits it and stays loopback-only.
|
|
22
25
|
*/
|
|
23
|
-
export declare const SANDBOX_CATCH_SCRIPT = "import json\nimport os\nimport random\nimport sys\nimport time\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\nfrom urllib.parse import unquote\n\nPORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8025\nOUT_FILE = sys.argv[2] if len(sys.argv) > 2 else \"/tmp/humanish-comms/deliveries.ndjson\"\nSERVED_DIR = sys.argv[3] if len(sys.argv) > 3 else (os.path.dirname(OUT_FILE) + \"/surface\")\ntry:\n os.makedirs(os.path.dirname(OUT_FILE), exist_ok=True)\nexcept Exception:\n pass\nMAX_BODY = 5 * 1024 * 1024\nCSP = \"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:\"\n\n\ndef message_id():\n return \"humanish-catch-\" + format(int(time.time() * 1000), \"x\") + format(random.randrange(16 ** 8), \"08x\")\n\n\nclass
|
|
26
|
+
export declare const SANDBOX_CATCH_SCRIPT = "import json\nimport os\nimport random\nimport sys\nimport threading\nimport time\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\nfrom urllib.parse import unquote\n\nPORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8025\nOUT_FILE = sys.argv[2] if len(sys.argv) > 2 else \"/tmp/humanish-comms/deliveries.ndjson\"\nSERVED_DIR = sys.argv[3] if len(sys.argv) > 3 else (os.path.dirname(OUT_FILE) + \"/surface\")\nINBOX_PORT = int(sys.argv[4]) if len(sys.argv) > 4 else 0\ntry:\n os.makedirs(os.path.dirname(OUT_FILE), exist_ok=True)\nexcept Exception:\n pass\nMAX_BODY = 5 * 1024 * 1024\nCSP = \"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:\"\n\n\ndef message_id():\n return \"humanish-catch-\" + format(int(time.time() * 1000), \"x\") + format(random.randrange(16 ** 8), \"08x\")\n\n\nclass BaseHandler(BaseHTTPRequestHandler):\n def log_message(self, *args):\n return\n\n def _json(self, status, obj, extra_headers=None):\n payload = json.dumps(obj).encode(\"utf-8\")\n self.send_response(status)\n self.send_header(\"content-type\", \"application/json; charset=utf-8\")\n for key, value in (extra_headers or {}).items():\n self.send_header(key, value)\n self.end_headers()\n self.wfile.write(payload)\n\n def do_GET(self):\n path = self.path.split(\"?\")[0]\n if path == \"/\" or path == \"/health\":\n self._json(200, {\"ok\": True, \"service\": \"humanish-comms-catch\"})\n return\n if path == \"/inbox\" or path.startswith(\"/inbox/\") or path == \"/api/inbox\" or path.startswith(\"/api/inbox/\"):\n rel = unquote(path)\n if \"..\" in rel or chr(0) in rel:\n self.send_response(400)\n self.end_headers()\n return\n data = None\n for candidate in (SERVED_DIR + rel, SERVED_DIR + rel + \"/index\"):\n try:\n with open(candidate, \"rb\") as handle:\n data = handle.read()\n break\n except Exception:\n data = None\n if data is None:\n self.send_response(404)\n self.send_header(\"content-type\", \"text/html; charset=utf-8\")\n self.end_headers()\n self.wfile.write(b\"<p>message not found</p>\")\n return\n is_api = rel.startswith(\"/api/\")\n self.send_response(200)\n self.send_header(\"content-type\", \"application/json; charset=utf-8\" if is_api else \"text/html; charset=utf-8\")\n if not is_api:\n self.send_header(\"content-security-policy\", CSP)\n self.end_headers()\n self.wfile.write(data)\n return\n self._json(404, {\"error\": \"not found\"})\n\n\nclass CaptureHandler(BaseHandler):\n def do_POST(self):\n path = self.path.split(\"?\")[0]\n try:\n length = int(self.headers.get(\"content-length\") or 0)\n except Exception:\n length = 0\n if length > MAX_BODY:\n self.send_response(413)\n self.end_headers()\n return\n body = self.rfile.read(length).decode(\"utf-8\", \"replace\") if length > 0 else \"\"\n try:\n with open(OUT_FILE, \"a\", encoding=\"utf-8\") as handle:\n print(json.dumps({\"t\": int(time.time() * 1000), \"path\": path, \"body\": body}), file=handle)\n except Exception:\n pass\n mid = message_id()\n if path == \"/v3/mail/send\":\n self.send_response(202)\n self.send_header(\"x-message-id\", mid)\n self.end_headers()\n elif path.endswith(\"/batch\"):\n self._json(200, {\"data\": [{\"id\": mid}]})\n else:\n self._json(200, {\"id\": mid})\n\n\nclass ReadOnlyHandler(BaseHandler):\n def do_POST(self):\n self.send_response(405)\n self.end_headers()\n\n\n# Optional read-only inbox listener on 0.0.0.0 (getHost-reachable from a DIFFERENT sandbox on the\n# shared-world route). Serves GET /inbox + /api/inbox + /health only; POST capture stays on the\n# 127.0.0.1 listener so nothing on the internet can inject a fake captured send. Started only when a\n# distinct inbox port is provided (the CUA same-sandbox route omits it and stays loopback-only).\nif INBOX_PORT and INBOX_PORT != PORT:\n threading.Thread(target=lambda: ThreadingHTTPServer((\"0.0.0.0\", INBOX_PORT), ReadOnlyHandler).serve_forever(), daemon=True).start()\n\nThreadingHTTPServer((\"127.0.0.1\", PORT), CaptureHandler).serve_forever()\n";
|
|
24
27
|
export interface DeployCommsCatchOptions {
|
|
25
28
|
/** Fixed loopback port the catch listens on (default 8025). Must be free inside the sandbox. */
|
|
26
29
|
port?: number;
|
|
30
|
+
/** Optional SECOND fixed port for a READ-ONLY inbox listener bound to 0.0.0.0, so a persona in a
|
|
31
|
+
* DIFFERENT sandbox can reach the inbox surface via getHost (the shared-world route). Omit on the
|
|
32
|
+
* CUA same-sandbox route (loopback is enough). Must differ from `port` and be free in the sandbox. */
|
|
33
|
+
inboxPort?: number;
|
|
27
34
|
/** In-sandbox working dir for the script + NDJSON (default /tmp/humanish-comms). */
|
|
28
35
|
dir?: string;
|
|
29
36
|
/** Detached-process name ([a-z0-9-]); default "comms-catch". */
|
|
@@ -41,6 +48,9 @@ export interface DeployedCommsCatch {
|
|
|
41
48
|
/** In-sandbox dir the HOST renders the persona-facing inbox-surface files into (via writeInboxSurface);
|
|
42
49
|
* the catch serves them at /inbox and /api/inbox. */
|
|
43
50
|
surfaceDir: string;
|
|
51
|
+
/** The 0.0.0.0 read-only inbox port, when one was requested — getHost-expose THIS to give a
|
|
52
|
+
* different-sandbox persona a reachable inbox URL. Absent on the loopback-only (CUA) route. */
|
|
53
|
+
inboxPort?: number;
|
|
44
54
|
/** Whether the catch's /health returned OUR service marker within the readiness budget. Callers MUST
|
|
45
55
|
* treat `ready === false` as fatal (do not inject baseUrl into a dead catch — the app's sends would
|
|
46
56
|
* silently fail with nothing captured). */
|
|
@@ -111,27 +121,32 @@ export declare function collectCommsThread(args: {
|
|
|
111
121
|
export declare function writeInboxSurface(desktop: E2BDesktopSandbox, surfaceDir: string, messages: CommsMessage[], options?: InboxRenderOptions & {
|
|
112
122
|
requestTimeoutMs?: number;
|
|
113
123
|
}): Promise<number>;
|
|
124
|
+
/** A declared inbox recipient the surface renders for (lane + the literal address the app sends to). */
|
|
125
|
+
export interface InboxSurfaceRecipient {
|
|
126
|
+
lane: string;
|
|
127
|
+
address: string;
|
|
128
|
+
}
|
|
114
129
|
/**
|
|
115
|
-
* One mid-run inbox-surface refresh cycle:
|
|
116
|
-
*
|
|
117
|
-
* persona sees new mail while the session is live. Returns the
|
|
130
|
+
* One mid-run inbox-surface refresh cycle: FULL rebuild from the append-only NDJSON (drain from cursor 0)
|
|
131
|
+
* into a FRESH FakeInbox each call, provisioning the declared `recipients`, then (re)render the surface
|
|
132
|
+
* so the persona sees new mail while the session is live. Returns the total captured-send `count` + whether
|
|
133
|
+
* it rendered.
|
|
118
134
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
135
|
+
* The full rebuild is deliberate — it is IDEMPOTENT and RETRY-SAFE: a transient writeInboxSurface failure
|
|
136
|
+
* PROPAGATES (the caller retries next tick without advancing its `sinceCount`), and because each rebuild
|
|
137
|
+
* starts from a clean channel, a send is never routed twice, so the persona never sees duplicate emails.
|
|
138
|
+
* Pass `sinceCount` (the last SUCCESSFULLY-rendered send count) to skip the (N-file) render when nothing
|
|
139
|
+
* new has arrived. This is independent of the teardown collectCommsThread drain (its own fresh channel,
|
|
140
|
+
* also cursor 0) — no evidence is lost or altered. The NDJSON is small for a run, so re-reading it is cheap.
|
|
125
141
|
*/
|
|
126
142
|
export declare function refreshInboxSurface(args: {
|
|
127
143
|
desktop: E2BDesktopSandbox;
|
|
128
144
|
deployed: Pick<DeployedCommsCatch, "deliveriesPath" | "surfaceDir">;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
cursor: number;
|
|
145
|
+
recipients: InboxSurfaceRecipient[];
|
|
146
|
+
sinceCount?: number;
|
|
132
147
|
originMap?: InboxRenderOptions["originMap"];
|
|
133
148
|
requestTimeoutMs?: number;
|
|
134
149
|
}): Promise<{
|
|
135
|
-
|
|
150
|
+
count: number;
|
|
136
151
|
rendered: boolean;
|
|
137
152
|
}>;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// each poll `cat` its append-only NDJSON of captured sends back to the host, where the real profiles
|
|
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
|
+
import { FakeInbox } from "./comms-fake-inbox.js";
|
|
9
10
|
import { buildCommsThreadArtifact } from "./comms-evidence.js";
|
|
10
11
|
import { buildInboxSurface } from "./comms-inbox.js";
|
|
11
12
|
import { DEFAULT_EMAIL_PROFILES } from "./comms-email-catch.js";
|
|
@@ -28,13 +29,17 @@ function shq(value) {
|
|
|
28
29
|
* and returns a plausible provider success — all normalization/profile parsing happens host-side on the
|
|
29
30
|
* drained lines, so the typed, tested profiles stay in one place. It also serves the host-rendered inbox
|
|
30
31
|
* surface statically at /inbox + /api/inbox (with a script-forbidding CSP). argv: <port> <deliveriesFile>
|
|
31
|
-
* [
|
|
32
|
-
*
|
|
32
|
+
* <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback) — the app under test reaches
|
|
33
|
+
* it in-sandbox, nothing on the internet can inject a fake send. When an [inboxPort] is given (the
|
|
34
|
+
* shared-world route, where the persona lives in a DIFFERENT sandbox), it ALSO starts a READ-ONLY inbox
|
|
35
|
+
* listener on 0.0.0.0:<inboxPort> so getHost can proxy the persona's inbox reads to it; that listener
|
|
36
|
+
* serves GET only (POST → 405). The CUA same-sandbox route omits it and stays loopback-only.
|
|
33
37
|
*/
|
|
34
38
|
export const SANDBOX_CATCH_SCRIPT = `import json
|
|
35
39
|
import os
|
|
36
40
|
import random
|
|
37
41
|
import sys
|
|
42
|
+
import threading
|
|
38
43
|
import time
|
|
39
44
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
40
45
|
from urllib.parse import unquote
|
|
@@ -42,6 +47,7 @@ from urllib.parse import unquote
|
|
|
42
47
|
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8025
|
|
43
48
|
OUT_FILE = sys.argv[2] if len(sys.argv) > 2 else "/tmp/humanish-comms/deliveries.ndjson"
|
|
44
49
|
SERVED_DIR = sys.argv[3] if len(sys.argv) > 3 else (os.path.dirname(OUT_FILE) + "/surface")
|
|
50
|
+
INBOX_PORT = int(sys.argv[4]) if len(sys.argv) > 4 else 0
|
|
45
51
|
try:
|
|
46
52
|
os.makedirs(os.path.dirname(OUT_FILE), exist_ok=True)
|
|
47
53
|
except Exception:
|
|
@@ -54,7 +60,7 @@ def message_id():
|
|
|
54
60
|
return "humanish-catch-" + format(int(time.time() * 1000), "x") + format(random.randrange(16 ** 8), "08x")
|
|
55
61
|
|
|
56
62
|
|
|
57
|
-
class
|
|
63
|
+
class BaseHandler(BaseHTTPRequestHandler):
|
|
58
64
|
def log_message(self, *args):
|
|
59
65
|
return
|
|
60
66
|
|
|
@@ -102,6 +108,8 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
102
108
|
return
|
|
103
109
|
self._json(404, {"error": "not found"})
|
|
104
110
|
|
|
111
|
+
|
|
112
|
+
class CaptureHandler(BaseHandler):
|
|
105
113
|
def do_POST(self):
|
|
106
114
|
path = self.path.split("?")[0]
|
|
107
115
|
try:
|
|
@@ -129,7 +137,20 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
129
137
|
self._json(200, {"id": mid})
|
|
130
138
|
|
|
131
139
|
|
|
132
|
-
|
|
140
|
+
class ReadOnlyHandler(BaseHandler):
|
|
141
|
+
def do_POST(self):
|
|
142
|
+
self.send_response(405)
|
|
143
|
+
self.end_headers()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Optional read-only inbox listener on 0.0.0.0 (getHost-reachable from a DIFFERENT sandbox on the
|
|
147
|
+
# shared-world route). Serves GET /inbox + /api/inbox + /health only; POST capture stays on the
|
|
148
|
+
# 127.0.0.1 listener so nothing on the internet can inject a fake captured send. Started only when a
|
|
149
|
+
# distinct inbox port is provided (the CUA same-sandbox route omits it and stays loopback-only).
|
|
150
|
+
if INBOX_PORT and INBOX_PORT != PORT:
|
|
151
|
+
threading.Thread(target=lambda: ThreadingHTTPServer(("0.0.0.0", INBOX_PORT), ReadOnlyHandler).serve_forever(), daemon=True).start()
|
|
152
|
+
|
|
153
|
+
ThreadingHTTPServer(("127.0.0.1", PORT), CaptureHandler).serve_forever()
|
|
133
154
|
`;
|
|
134
155
|
/** Readiness probe that asserts OUR service marker in the /health body (not merely any 2xx) — so a
|
|
135
156
|
* process squatting on the fixed port cannot produce a false "ready" while the app's sends bypass us. */
|
|
@@ -159,6 +180,10 @@ export async function deployCommsCatch(desktop, options = {}) {
|
|
|
159
180
|
if (!Number.isInteger(port) || port <= 0 || port > 65_535) {
|
|
160
181
|
throw new Error(`deployCommsCatch: invalid port ${JSON.stringify(options.port)}`);
|
|
161
182
|
}
|
|
183
|
+
const inboxPort = options.inboxPort === undefined ? undefined : Math.trunc(Number(options.inboxPort));
|
|
184
|
+
if (inboxPort !== undefined && (!Number.isInteger(inboxPort) || inboxPort <= 0 || inboxPort > 65_535 || inboxPort === port)) {
|
|
185
|
+
throw new Error(`deployCommsCatch: invalid inboxPort ${JSON.stringify(options.inboxPort)}`);
|
|
186
|
+
}
|
|
162
187
|
const dir = options.dir ?? DEFAULT_CATCH_DIR;
|
|
163
188
|
const name = options.name ?? "comms-catch";
|
|
164
189
|
const requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
@@ -169,15 +194,22 @@ export async function deployCommsCatch(desktop, options = {}) {
|
|
|
169
194
|
await desktop.files.write(scriptPath, SANDBOX_CATCH_SCRIPT);
|
|
170
195
|
await startDetachedProcess(desktop, {
|
|
171
196
|
name,
|
|
172
|
-
command: `python3 ${shq(scriptPath)} ${port} ${shq(deliveriesPath)} ${shq(surfaceDir)}`,
|
|
197
|
+
command: `python3 ${shq(scriptPath)} ${port} ${shq(deliveriesPath)} ${shq(surfaceDir)}${inboxPort === undefined ? "" : ` ${inboxPort}`}`,
|
|
173
198
|
requestTimeoutMs
|
|
174
199
|
});
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return {
|
|
200
|
+
const probe = { timeoutMs: options.readyTimeoutMs ?? 15_000, requestTimeoutMs, ...(options.timers ?? {}) };
|
|
201
|
+
const ready = await catchHealthy(desktop, port, probe);
|
|
202
|
+
// Confirm the read-only inbox listener bound too (loopback-reachable at its own port), when requested —
|
|
203
|
+
// else a getHost-exposed inbox would 502. Fail closed by folding it into `ready`.
|
|
204
|
+
const inboxReady = inboxPort === undefined ? true : await catchHealthy(desktop, inboxPort, probe);
|
|
205
|
+
return {
|
|
206
|
+
port,
|
|
207
|
+
baseUrl: `http://127.0.0.1:${port}`,
|
|
208
|
+
deliveriesPath,
|
|
209
|
+
surfaceDir,
|
|
210
|
+
...(inboxPort === undefined ? {} : { inboxPort }),
|
|
211
|
+
ready: ready && inboxReady
|
|
212
|
+
};
|
|
181
213
|
}
|
|
182
214
|
/**
|
|
183
215
|
* Drain new captured sends from the in-sandbox NDJSON since `cursor` (a line count). Returns the fresh
|
|
@@ -291,26 +323,33 @@ export async function writeInboxSurface(desktop, surfaceDir, messages, options =
|
|
|
291
323
|
return files.length;
|
|
292
324
|
}
|
|
293
325
|
/**
|
|
294
|
-
* One mid-run inbox-surface refresh cycle:
|
|
295
|
-
*
|
|
296
|
-
* persona sees new mail while the session is live. Returns the
|
|
326
|
+
* One mid-run inbox-surface refresh cycle: FULL rebuild from the append-only NDJSON (drain from cursor 0)
|
|
327
|
+
* into a FRESH FakeInbox each call, provisioning the declared `recipients`, then (re)render the surface
|
|
328
|
+
* so the persona sees new mail while the session is live. Returns the total captured-send `count` + whether
|
|
329
|
+
* it rendered.
|
|
297
330
|
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
331
|
+
* The full rebuild is deliberate — it is IDEMPOTENT and RETRY-SAFE: a transient writeInboxSurface failure
|
|
332
|
+
* PROPAGATES (the caller retries next tick without advancing its `sinceCount`), and because each rebuild
|
|
333
|
+
* starts from a clean channel, a send is never routed twice, so the persona never sees duplicate emails.
|
|
334
|
+
* Pass `sinceCount` (the last SUCCESSFULLY-rendered send count) to skip the (N-file) render when nothing
|
|
335
|
+
* new has arrived. This is independent of the teardown collectCommsThread drain (its own fresh channel,
|
|
336
|
+
* also cursor 0) — no evidence is lost or altered. The NDJSON is small for a run, so re-reading it is cheap.
|
|
304
337
|
*/
|
|
305
338
|
export async function refreshInboxSurface(args) {
|
|
306
|
-
const { sends
|
|
339
|
+
const { sends } = await drainCommsCatch(args.desktop, args.deployed, 0, args.requestTimeoutMs);
|
|
307
340
|
if (sends.length === 0)
|
|
308
|
-
return {
|
|
309
|
-
|
|
341
|
+
return { count: 0, rendered: false };
|
|
342
|
+
if (args.sinceCount !== undefined && sends.length <= args.sinceCount)
|
|
343
|
+
return { count: sends.length, rendered: false };
|
|
344
|
+
const channel = new FakeInbox();
|
|
345
|
+
const inboxes = [];
|
|
346
|
+
for (const recipient of args.recipients)
|
|
347
|
+
inboxes.push(await channel.provisionAddress(recipient.lane, recipient.address));
|
|
348
|
+
await routeCapturedSends(sends, channel);
|
|
310
349
|
const seen = new Set();
|
|
311
350
|
const messages = [];
|
|
312
|
-
for (const inbox of
|
|
313
|
-
for (const message of await
|
|
351
|
+
for (const inbox of inboxes) {
|
|
352
|
+
for (const message of await channel.poll(inbox, 0)) {
|
|
314
353
|
if (seen.has(message.id))
|
|
315
354
|
continue;
|
|
316
355
|
seen.add(message.id);
|
|
@@ -318,12 +357,12 @@ export async function refreshInboxSurface(args) {
|
|
|
318
357
|
}
|
|
319
358
|
}
|
|
320
359
|
if (messages.length === 0)
|
|
321
|
-
return {
|
|
360
|
+
return { count: sends.length, rendered: false };
|
|
322
361
|
messages.sort((a, b) => a.deliveredAt - b.deliveredAt || a.id.localeCompare(b.id));
|
|
323
362
|
await writeInboxSurface(args.desktop, args.deployed.surfaceDir, messages, {
|
|
324
363
|
...(args.originMap === undefined ? {} : { originMap: args.originMap }),
|
|
325
364
|
...(args.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: args.requestTimeoutMs })
|
|
326
365
|
});
|
|
327
|
-
return {
|
|
366
|
+
return { count: sends.length, rendered: true };
|
|
328
367
|
}
|
|
329
368
|
//# 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,iBAAiB,EAA2B,MAAM,kBAAkB,CAAC;AAC9E,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
|
|
1
|
+
{"version":3,"file":"comms-sandbox-catch.js","sourceRoot":"","sources":["../src/comms-sandbox-catch.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,9 +39,10 @@ import { adapterScoreFailureMessage, applyBrowserAdapterHooks } from "./adapter-
|
|
|
39
39
|
import { actorRegistry, isCuaActorDescriptor } from "./actor-registry.js";
|
|
40
40
|
import { toErrorMessage } from "./command-failure.js";
|
|
41
41
|
import { mapWithConcurrency } from "./concurrency.js";
|
|
42
|
-
import { commandDigestOf, composeLaneInstructions, defaultPackLocalTree, provisionCloneSubject, provisionLocalTreeSubject, declaredScreenForRender, resolveLaneDevice, resolveSubjectState, runCuaLane } from "./cua-actor-lab.js";
|
|
43
|
-
import { DEFAULT_SANDBOX_CATCH_PORT, collectCommsThread, deployCommsCatch } from "./comms-sandbox-catch.js";
|
|
42
|
+
import { commandDigestOf, composeLaneInstructions, defaultPackLocalTree, provisionCloneSubject, provisionLocalTreeSubject, declaredScreenForRender, laneHasInboxRecipient, resolveLaneDevice, resolveSubjectState, runCuaLane, withInboxMission } from "./cua-actor-lab.js";
|
|
43
|
+
import { DEFAULT_SANDBOX_CATCH_PORT, collectCommsThread, deployCommsCatch, refreshInboxSurface, writeInboxSurface } from "./comms-sandbox-catch.js";
|
|
44
44
|
import { FakeInbox } from "./comms-fake-inbox.js";
|
|
45
|
+
import { buildOriginMap } from "./comms-inbox.js";
|
|
45
46
|
import { createDesktopSandbox, loadE2BDesktopModule } from "./e2b-desktop-launch.js";
|
|
46
47
|
import { concurrentSharedWorldValidationReason, externalPublicSharedWorldValidationReason } from "./lab-config.js";
|
|
47
48
|
import { buildObserverData } from "./observer-data.js";
|
|
@@ -558,6 +559,13 @@ export async function runConcurrentSharedWorld(options) {
|
|
|
558
559
|
let subjectSandboxId;
|
|
559
560
|
let subjectKilled = false;
|
|
560
561
|
let getHostUrl;
|
|
562
|
+
// Persona inbox SURFACE (#297 slice B, shared-world): the getHost-exposed inbox URL a persona (in a
|
|
563
|
+
// DIFFERENT sandbox) opens, the serve->getHost origin-rewrite map (REQUIRED here so the app's loopback
|
|
564
|
+
// verify links resolve to a reachable host), and the dedicated surface channel + render loop.
|
|
565
|
+
let commsInboxUrl;
|
|
566
|
+
let commsOriginMap = [];
|
|
567
|
+
let surfaceRenderedCount = 0;
|
|
568
|
+
let surfaceLoop;
|
|
561
569
|
let runError;
|
|
562
570
|
let snapshotIndex = 0;
|
|
563
571
|
let liveObserver;
|
|
@@ -698,9 +706,11 @@ export async function runConcurrentSharedWorld(options) {
|
|
|
698
706
|
// (injected into its env at create) resolves the moment it boots. Fail closed if the catch can't
|
|
699
707
|
// stand up rather than let a comms-declared app silently send real mail to the internet.
|
|
700
708
|
if (commsEmail && commsPort !== undefined) {
|
|
701
|
-
|
|
709
|
+
// A SECOND (0.0.0.0) read-only inbox listener on commsPort+1 so the persona — which lives in a
|
|
710
|
+
// DIFFERENT sandbox here — can reach the inbox surface via getHost; capture stays loopback.
|
|
711
|
+
deployedComms = await deployCommsCatch(subjectDesktop, { port: commsPort, inboxPort: commsPort + 1, requestTimeoutMs, timers });
|
|
702
712
|
if (!deployedComms.ready) {
|
|
703
|
-
throw new Error(`comms email catch did not become ready
|
|
713
|
+
throw new Error(`comms email catch did not become ready in the subject sandbox (loopback capture ${commsPort} / inbox ${commsPort + 1})`);
|
|
704
714
|
}
|
|
705
715
|
}
|
|
706
716
|
// Provision the ONE shared plane: clone + install/build + seed + serve on 0.0.0.0 + probe
|
|
@@ -748,6 +758,61 @@ export async function runConcurrentSharedWorld(options) {
|
|
|
748
758
|
throw new Error("getHost returned a non-tokenless URL; refusing to persist a host URL that may carry a credential (invariant 1)");
|
|
749
759
|
}
|
|
750
760
|
getHostUrl = hostUrl;
|
|
761
|
+
// Persona inbox SURFACE (#297 slice B, shared-world): getHost-expose the read-only inbox listener so
|
|
762
|
+
// a persona in a DIFFERENT sandbox can open it; build the serve->getHost origin map (REQUIRED here —
|
|
763
|
+
// the app's loopback verify links must be rewritten to a reachable host); provision the surface
|
|
764
|
+
// channel; write the EMPTY inbox up front (so /inbox never 404s); and start a render loop that drains
|
|
765
|
+
// + re-renders on a cadence. The loop shares the prober's dispose signal (disposed together, before
|
|
766
|
+
// the teardown evidence drain), and uses a DEDICATED FakeInbox + cursor (independent of that drain).
|
|
767
|
+
if (commsEmail && deployedComms?.inboxPort !== undefined) {
|
|
768
|
+
const rawInboxHost = subjectDesktop.getHost(deployedComms.inboxPort);
|
|
769
|
+
const inboxHostUrl = /^https?:\/\//i.test(rawInboxHost) ? rawInboxHost : `https://${rawInboxHost}`;
|
|
770
|
+
if (!isTokenlessHost(inboxHostUrl)) {
|
|
771
|
+
throw new Error("getHost returned a non-tokenless URL for the comms inbox; refusing to advertise it (invariant 1)");
|
|
772
|
+
}
|
|
773
|
+
commsInboxUrl = `${inboxHostUrl}/inbox`;
|
|
774
|
+
commsOriginMap = buildOriginMap({
|
|
775
|
+
internalServeUrl: serve.url,
|
|
776
|
+
reachableBaseUrl: getHostUrl,
|
|
777
|
+
...(commsEmail.linkOrigin === undefined ? {} : { linkOrigin: commsEmail.linkOrigin })
|
|
778
|
+
});
|
|
779
|
+
const surfaceRecipients = (commsEmail.recipients ?? [])
|
|
780
|
+
.filter((recipient) => recipient.address !== undefined)
|
|
781
|
+
.map((recipient) => ({ lane: recipient.lane, address: recipient.address }));
|
|
782
|
+
await writeInboxSurface(subjectDesktop, deployedComms.surfaceDir, [], { originMap: commsOriginMap, requestTimeoutMs });
|
|
783
|
+
const surfaceDeployed = deployedComms;
|
|
784
|
+
const surfaceCadenceMs = 2500;
|
|
785
|
+
surfaceLoop = (async () => {
|
|
786
|
+
// Full, idempotent rebuild each tick; surfaceRenderedCount advances only on a successful render,
|
|
787
|
+
// so a transient failure retries cleanly. Real timer (dispose-interruptible + cleared) — an
|
|
788
|
+
// unbounded loop must not busy-spin on the injected instant clock.
|
|
789
|
+
for (;;) {
|
|
790
|
+
try {
|
|
791
|
+
const refreshed = await refreshInboxSurface({
|
|
792
|
+
desktop: subjectDesktop,
|
|
793
|
+
deployed: surfaceDeployed,
|
|
794
|
+
recipients: surfaceRecipients,
|
|
795
|
+
sinceCount: surfaceRenderedCount,
|
|
796
|
+
originMap: commsOriginMap,
|
|
797
|
+
requestTimeoutMs
|
|
798
|
+
});
|
|
799
|
+
if (refreshed.rendered)
|
|
800
|
+
surfaceRenderedCount = refreshed.count;
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
// Never throw into the render loop; the teardown drain + by-id teardown must still run.
|
|
804
|
+
}
|
|
805
|
+
if (proberDisposed)
|
|
806
|
+
break;
|
|
807
|
+
await new Promise((resolve) => {
|
|
808
|
+
const timer = setTimeout(resolve, surfaceCadenceMs);
|
|
809
|
+
void disposeSignal.then(() => { clearTimeout(timer); resolve(); });
|
|
810
|
+
});
|
|
811
|
+
if (proberDisposed)
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
})();
|
|
815
|
+
}
|
|
751
816
|
// Baseline state snapshot, then start the background cadence prober.
|
|
752
817
|
await proberSnapshot();
|
|
753
818
|
if (options.onObserverReady) {
|
|
@@ -840,8 +905,13 @@ export async function runConcurrentSharedWorld(options) {
|
|
|
840
905
|
};
|
|
841
906
|
actorResults = await mapWithConcurrency(actorSpecs, Math.max(1, concurrency), async (spec, i) => {
|
|
842
907
|
const route = resolveActorSeatUrl(getHostUrl, roles[i]?.entry);
|
|
908
|
+
// Tell this persona its (getHost-reachable) inbox URL — but only when comms is live AND this lane
|
|
909
|
+
// has a declared recipient it can actually receive mail into (else it would stall on an empty inbox).
|
|
910
|
+
const laneSpec = commsEmail && commsInboxUrl && laneHasInboxRecipient(commsEmail, spec.laneId)
|
|
911
|
+
? withInboxMission(spec, commsInboxUrl)
|
|
912
|
+
: spec;
|
|
843
913
|
const startedAt = now();
|
|
844
|
-
const outcome = await runCuaLane(
|
|
914
|
+
const outcome = await runCuaLane(laneSpec, { ...baseActorDeps, appUrl: route });
|
|
845
915
|
const endedAt = now();
|
|
846
916
|
return { spec, outcome, startedAt, endedAt, route };
|
|
847
917
|
});
|
|
@@ -858,6 +928,11 @@ export async function runConcurrentSharedWorld(options) {
|
|
|
858
928
|
if (proberLoop) {
|
|
859
929
|
await proberLoop.catch(() => undefined);
|
|
860
930
|
}
|
|
931
|
+
// Stop the inbox-surface render loop too (shares the prober's dispose signal), before the teardown
|
|
932
|
+
// evidence drain below — so the two in-sandbox reads never overlap and the surface state is final.
|
|
933
|
+
if (surfaceLoop) {
|
|
934
|
+
await surfaceLoop.catch(() => undefined);
|
|
935
|
+
}
|
|
861
936
|
if (subjectDesktop && getHostUrl) {
|
|
862
937
|
await proberSnapshot().catch(() => undefined);
|
|
863
938
|
}
|