humanish 0.95.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 +4 -2
- 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 +4 -2
- package/dist/comms-connections.js +3 -3
- package/dist/comms-connections.js.map +1 -1
- 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.js +11 -0
- 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/lab-config.d.ts +20 -3
- package/dist/lab-config.js +59 -6
- 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 +1 -0
- package/dist/lab-summary.js +8 -2
- 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 +2 -0
- package/dist/program.js +100 -4
- 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/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 +134 -134
- package/dist/tui-contract.d.ts +13 -0
- 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 +20 -3
- package/docs/goals/current.md +5 -5
- package/docs/ramp/README.md +9 -5
- package/docs/release/0.96.0-real-email-receiving.md +47 -0
- package/package.json +2 -1
- package/skills/humanish/SKILL.md +33 -13
package/dist/cua-actor-lab.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { withTransientCommsSecrets } from "./run-narration-secrets.js";
|
|
2
|
+
import { prepareReceivingRun, receivingPublication } from "./comms-receiving-runtime.js";
|
|
3
|
+
import { deployReceivingInbox } from "./comms-receiving-inbox.js";
|
|
4
|
+
import { receivingEmailValidationReason } from "./lab-config.js";
|
|
1
5
|
// The computer-use lab backend: a subject (an app-url the caller provisioned, or a repo the
|
|
2
6
|
// lab clones AND serves in-sandbox) driven by a REGISTRY-RESOLVED computer-use actor inside a
|
|
3
7
|
// hosted E2B desktop. This is the path that makes `actors[].type` load-bearing — the
|
|
@@ -237,7 +241,7 @@ export function composeLaneInstructions(args) {
|
|
|
237
241
|
* runtime loopback/getHost address (not secret), so — mirroring the lobby-code runtime injection — this
|
|
238
242
|
* augments only the instructions the model receives; the authored prompt + its digest are unchanged.
|
|
239
243
|
* Returns a new spec (never mutates). Shared by the CUA + concurrent shared-world routes. */
|
|
240
|
-
export function withInboxMission(spec, inboxUrl, address) {
|
|
244
|
+
export function withInboxMission(spec, inboxUrl, address, receiving = false) {
|
|
241
245
|
// No assigned identity means no participant inbox; never fall back to the shared operator view.
|
|
242
246
|
if (!address?.trim())
|
|
243
247
|
return spec;
|
|
@@ -248,6 +252,11 @@ export function withInboxMission(spec, inboxUrl, address) {
|
|
|
248
252
|
// and ends its session — the exact give-up class a live run documented — unless told the wait
|
|
249
253
|
// is expected and the inbox is the next step.
|
|
250
254
|
const identity = ` Your email address is ${address} — when the app asks for an email address, enter exactly that.`;
|
|
255
|
+
if (receiving)
|
|
256
|
+
return {
|
|
257
|
+
...spec,
|
|
258
|
+
instructions: `${spec.instructions}\n\nEmail inbox:${identity} This is a fresh test identity; it does not replace an existing account's email address. When the app says it sent email, open ${inboxUrl} to check your inbox. Read the original email and use its verification link or code. Delivery may take a little time; refresh if needed. If mail remains missing or unavailable, report what you observed rather than assuming the app failed to send. The inbox may block remote images or undeclared destinations; those are harness limitations.`
|
|
259
|
+
};
|
|
251
260
|
return {
|
|
252
261
|
...spec,
|
|
253
262
|
instructions: `${spec.instructions}\n\nEmail inbox:${identity} When the app tells you it has emailed you (a verification link, confirmation code, or magic link), open ${recipientInboxUrl(inboxUrl, address)} in the browser to read that email and follow its link or enter its code. All email the app sends you arrives there. Waiting for an email is normal, not a blocker — do not end your session while waiting; open the inbox and refresh it until the email appears.`
|
|
@@ -1653,12 +1662,13 @@ export async function runCuaLane(spec, deps) {
|
|
|
1653
1662
|
// in-sandbox catch (loopback) so its verification mail is CAPTURED, not sent to the internet. Gated
|
|
1654
1663
|
// ENTIRELY on config.comms — no comms declared → zero change. The base-URL env is injected at
|
|
1655
1664
|
// sandbox-create (below, so the app reads it at boot); the catch is started right after create.
|
|
1656
|
-
const commsEmail = (cloneRoute || localTreeRoute)
|
|
1665
|
+
const commsEmail = (cloneRoute || localTreeRoute) && config.comms?.email?.kind === "fake" ? config.comms.email : undefined;
|
|
1657
1666
|
const commsPort = commsEmail ? (commsEmail.port ?? DEFAULT_SANDBOX_CATCH_PORT) : undefined;
|
|
1658
1667
|
// Hoisted so the finally can drain the catch before teardown; `commsArtifactPath` is the written
|
|
1659
1668
|
// evidence path folded into the lane outcome.
|
|
1660
1669
|
let deployedComms;
|
|
1661
1670
|
let commsArtifactPath;
|
|
1671
|
+
let receivingInboxUrl;
|
|
1662
1672
|
// injectEnv is absent on an adopter-hosted plane (#328): there is no subject env to inject
|
|
1663
1673
|
// because the operator points their own app at their own catch.
|
|
1664
1674
|
const commsEnv = commsEmail?.injectEnv !== undefined && commsPort !== undefined
|
|
@@ -1810,6 +1820,27 @@ export async function runCuaLane(spec, deps) {
|
|
|
1810
1820
|
// Start the in-sandbox email catch BEFORE the subject serve, so the app's send-API base URL (injected
|
|
1811
1821
|
// into its env at create) resolves the moment it boots. A comms-declared lab that can't stand the
|
|
1812
1822
|
// catch up is a setup failure (fail closed) rather than silently sending real mail.
|
|
1823
|
+
if (deps.receiving) {
|
|
1824
|
+
const surface = await deployReceivingInbox(desktop, { leaseId: spec.streamId, requestTimeoutMs: Math.min(deps.requestTimeoutMs, 30_000) });
|
|
1825
|
+
receivingInboxUrl = surface.url;
|
|
1826
|
+
const email = config.comms?.email;
|
|
1827
|
+
try {
|
|
1828
|
+
await deps.receiving.attach(spec.laneId, {
|
|
1829
|
+
surface,
|
|
1830
|
+
allowedOrigins: [...new Set([new URL(targetUrl).origin, ...(email?.allowedOrigins ?? [])])],
|
|
1831
|
+
originMap: buildOriginMap({
|
|
1832
|
+
...(config.subject.serve?.url === undefined ? {} : { internalServeUrl: config.subject.serve.url }),
|
|
1833
|
+
reachableBaseUrl: targetUrl,
|
|
1834
|
+
...(email?.linkOrigin === undefined ? {} : { linkOrigin: email.linkOrigin })
|
|
1835
|
+
})
|
|
1836
|
+
});
|
|
1837
|
+
commsArtifactPath = "comms/receiving.json";
|
|
1838
|
+
}
|
|
1839
|
+
catch (error) {
|
|
1840
|
+
await surface.stop().catch(() => { });
|
|
1841
|
+
throw error;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1813
1844
|
if (commsEmail && commsPort !== undefined) {
|
|
1814
1845
|
deployedComms = await deployCommsCatch(desktop, {
|
|
1815
1846
|
port: commsPort,
|
|
@@ -2098,11 +2129,13 @@ export async function runCuaLane(spec, deps) {
|
|
|
2098
2129
|
// recipient it can actually receive mail into (else it would stall on an inbox that stays
|
|
2099
2130
|
// empty). Two comms planes, mutually exclusive by parse: the in-sandbox catch humanish
|
|
2100
2131
|
// deployed, or the adopter-hosted one (#380).
|
|
2101
|
-
instructions:
|
|
2102
|
-
? withInboxMission(spec,
|
|
2103
|
-
:
|
|
2104
|
-
? withInboxMission(spec,
|
|
2105
|
-
: spec.
|
|
2132
|
+
instructions: deps.receiving && receivingInboxUrl
|
|
2133
|
+
? withInboxMission(spec, receivingInboxUrl, deps.receiving.address(spec.laneId), true).instructions
|
|
2134
|
+
: commsEmail && commsInboxUrl && deployedComms?.ready && laneHasInboxRecipient(commsEmail, spec.laneId)
|
|
2135
|
+
? withInboxMission(spec, commsInboxUrl, inboxRecipientFor(commsEmail, spec.laneId)?.address).instructions
|
|
2136
|
+
: deps.externalComms && laneHasInboxRecipient(deps.externalComms.email, spec.laneId)
|
|
2137
|
+
? withInboxMission(spec, deps.externalComms.inboxUrl, inboxRecipientFor(deps.externalComms.email, spec.laneId)?.address).instructions
|
|
2138
|
+
: spec.instructions,
|
|
2106
2139
|
persona: spec.persona,
|
|
2107
2140
|
timeoutMs: deps.timeoutMs,
|
|
2108
2141
|
// The brain is either a keyed API client or a CLI the operator is already signed in to.
|
|
@@ -2257,6 +2290,14 @@ export async function runCuaLane(spec, deps) {
|
|
|
2257
2290
|
: { warnings: [...(desktopGeometry.warnings ?? []), ...geometryWarnings] })
|
|
2258
2291
|
};
|
|
2259
2292
|
}
|
|
2293
|
+
if (deps.receiving) {
|
|
2294
|
+
try {
|
|
2295
|
+
await deps.receiving.finishParticipant(spec.laneId);
|
|
2296
|
+
}
|
|
2297
|
+
catch {
|
|
2298
|
+
warnings.push("Real email finalization is incomplete. Inspect communication cleanup with humanish comms recover.");
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2260
2301
|
// Off-app comms evidence (#297): before this lane's sandbox is torn down, drain everything the
|
|
2261
2302
|
// in-sandbox catch captured, route it into a host fake inbox addressed to the declared
|
|
2262
2303
|
// recipients, and write the digest-only thread artifact. Wrapped so a drain failure NEVER
|
|
@@ -2700,6 +2741,9 @@ function subjectProvenanceArg(subject, publicRepo, subjectEnvNames) {
|
|
|
2700
2741
|
* ticking into a directory something else is deleting, which surfaces as an unrelated ENOTEMPTY.
|
|
2701
2742
|
*/
|
|
2702
2743
|
export async function runCuaActorLab(options) {
|
|
2744
|
+
return withTransientCommsSecrets(() => runCuaActorLabWithSecrets(options));
|
|
2745
|
+
}
|
|
2746
|
+
async function runCuaActorLabWithSecrets(options) {
|
|
2703
2747
|
const analysisReason = resolveAutomaticAnalysis(options.config.review?.analysis);
|
|
2704
2748
|
const tasksReason = analysisReason.ok ? taskProtocolValidationReason(options.config, true) : analysisReason.message;
|
|
2705
2749
|
if (tasksReason)
|
|
@@ -2792,7 +2836,12 @@ async function runCuaActorLabInScope(options) {
|
|
|
2792
2836
|
if (actor?.maxOutputTokens !== undefined && (hooks.runSession || hooks.buildProvider || hooks.buildExecutor)) {
|
|
2793
2837
|
return fail("HUMANISH_CUA_LAB_SUBJECT_INVALID", "maxOutputTokens cannot be enforced by a custom runSession/provider/executor route.", descriptor.id);
|
|
2794
2838
|
}
|
|
2839
|
+
const receivingReason = receivingEmailValidationReason(config);
|
|
2840
|
+
if (receivingReason)
|
|
2841
|
+
return fail("HUMANISH_CUA_LAB_SUBJECT_INVALID", receivingReason, descriptor.id);
|
|
2795
2842
|
const inProcessRoute = hooks.buildExecutor !== undefined;
|
|
2843
|
+
if (inProcessRoute && config.comms?.email?.kind === "real")
|
|
2844
|
+
return fail("HUMANISH_CUA_LAB_SUBJECT_INVALID", "Real email receiving requires hosted participant desktops.", descriptor.id);
|
|
2796
2845
|
if (inProcessRoute && config.execution?.desktop?.media !== undefined) {
|
|
2797
2846
|
return fail("HUMANISH_CUA_LAB_SUBJECT_INVALID", "execution.desktop.media is not provisioned by a caller-supplied executor. Remove the declaration or use a hosted computer-use browser lane.", descriptor.id);
|
|
2798
2847
|
}
|
|
@@ -3267,20 +3316,47 @@ async function runCuaActorLabInScope(options) {
|
|
|
3267
3316
|
await flushWriting;
|
|
3268
3317
|
};
|
|
3269
3318
|
}
|
|
3319
|
+
const receivingWarnings = [];
|
|
3320
|
+
let receiving;
|
|
3321
|
+
if (!dryRun && config.comms?.email?.kind === "real") {
|
|
3322
|
+
try {
|
|
3323
|
+
receiving = await prepareReceivingRun({ cwd, runId, config, env, participants: laneSpecs.map(spec => spec.laneId), runPaths,
|
|
3324
|
+
registerSecrets: values => { for (const value of values)
|
|
3325
|
+
if (value.length >= 4 && !knownSecretValues.includes(value))
|
|
3326
|
+
knownSecretValues.push(value); }
|
|
3327
|
+
});
|
|
3328
|
+
if (receiving)
|
|
3329
|
+
deps.receiving = receiving;
|
|
3330
|
+
}
|
|
3331
|
+
catch {
|
|
3332
|
+
await stopLiveFlush?.();
|
|
3333
|
+
return fail("HUMANISH_CUA_LAB_SUBJECT_INVALID", "Real email setup failed before desktop allocation. Run humanish comms check --online and humanish comms recover to inspect authentication and pending cleanup.", descriptor.id);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3270
3336
|
// Run lanes (dry-run runs none). In-process is always one lane.
|
|
3271
3337
|
let outcomes;
|
|
3272
3338
|
let failFastReason;
|
|
3273
|
-
|
|
3274
|
-
if (
|
|
3275
|
-
|
|
3339
|
+
try {
|
|
3340
|
+
if (!dryRun) {
|
|
3341
|
+
if (inProcessRoute) {
|
|
3342
|
+
outcomes = [await runInProcessLane(laneSpecs[0], deps)];
|
|
3343
|
+
}
|
|
3344
|
+
else if (laneCount === 1) {
|
|
3345
|
+
outcomes = [await runCuaLane(laneSpecs[0], deps)];
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
const ran = await runCuaLanes(laneSpecs, deps, plan.concurrency);
|
|
3349
|
+
outcomes = ran.outcomes;
|
|
3350
|
+
failFastReason = ran.failFastReason;
|
|
3351
|
+
}
|
|
3276
3352
|
}
|
|
3277
|
-
|
|
3278
|
-
|
|
3353
|
+
}
|
|
3354
|
+
finally {
|
|
3355
|
+
try {
|
|
3356
|
+
await receiving?.finish();
|
|
3279
3357
|
}
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
outcomes = ran.outcomes;
|
|
3283
|
-
failFastReason = ran.failFastReason;
|
|
3358
|
+
catch {
|
|
3359
|
+
receivingWarnings.push("Email finalization could not complete. Inspect humanish comms recover; provider cleanup remains unresolved.");
|
|
3284
3360
|
}
|
|
3285
3361
|
}
|
|
3286
3362
|
// Close the live flush BEFORE any final artifact work: no new flush may start, and an
|
|
@@ -3438,6 +3514,8 @@ async function runCuaActorLabInScope(options) {
|
|
|
3438
3514
|
hookLabel: "cuaHooks",
|
|
3439
3515
|
...(options.scorerProvenance === undefined ? {} : { scorerProvenance: options.scorerProvenance })
|
|
3440
3516
|
});
|
|
3517
|
+
if (receiving)
|
|
3518
|
+
bundle.commsReceiving = receiving.snapshot();
|
|
3441
3519
|
await writeCuaRunArtifacts(bundle, createdAt, runPaths);
|
|
3442
3520
|
// Finalize the status record from the bundle that was just written, so the index can never
|
|
3443
3521
|
// claim an outcome the evidence does not carry. A run that throws before reaching here leaves
|
|
@@ -3467,7 +3545,7 @@ async function runCuaActorLabInScope(options) {
|
|
|
3467
3545
|
const adapterFailure = adapterScoreFailureMessage(bundle);
|
|
3468
3546
|
const ok = observer.ok && allLanesOk && adapterFailure === undefined && scorerResult.declaredVerdictFailure === undefined;
|
|
3469
3547
|
const laneWarnings = (outcomes ?? []).flatMap((outcome) => outcome.warnings);
|
|
3470
|
-
const warnings = [...laneWarnings, ...aggregateWarnings, ...adapterWarnings, ...observer.warnings];
|
|
3548
|
+
const warnings = [...receivingWarnings, ...laneWarnings, ...aggregateWarnings, ...adapterWarnings, ...observer.warnings];
|
|
3471
3549
|
const laneResults = laneSpecs.map((spec, index) => toLaneResult(spec, outcomes?.[index], laneSubjects[index], dryRun));
|
|
3472
3550
|
const laneSummary = buildLaneSummary(outcomes, laneCount, plan, dryRun);
|
|
3473
3551
|
const firstOutcome = outcomes?.[0];
|
|
@@ -3625,6 +3703,7 @@ function observerResultForCuaArtifacts(cwd, runId, artifactRoot, warnings = [])
|
|
|
3625
3703
|
function buildSingleLaneBundle(args) {
|
|
3626
3704
|
const { spec, outcome, config } = args;
|
|
3627
3705
|
return buildCuaBundle({
|
|
3706
|
+
realEmail: config.comms?.email?.kind === "real",
|
|
3628
3707
|
...(args.lab === undefined ? {} : { lab: args.lab }),
|
|
3629
3708
|
actorId: args.descriptor.id,
|
|
3630
3709
|
appUrl: args.appUrl,
|
|
@@ -4520,6 +4599,7 @@ export function buildCuaBundle(args) {
|
|
|
4520
4599
|
}, [stream]);
|
|
4521
4600
|
return {
|
|
4522
4601
|
schema: RUN_BUNDLE_SCHEMA,
|
|
4602
|
+
...(args.realEmail && !args.dryRun ? { publication: { restrictions: ["real-communications"] } } : {}),
|
|
4523
4603
|
runId: args.runId,
|
|
4524
4604
|
mode: args.dryRun ? "dry-run" : "live",
|
|
4525
4605
|
simCount: 1,
|
|
@@ -5008,6 +5088,7 @@ export function buildCuaFanoutBundle(args) {
|
|
|
5008
5088
|
const cost = buildCuaCostSummary({ lanes: costLanes, desktops });
|
|
5009
5089
|
return {
|
|
5010
5090
|
schema: RUN_BUNDLE_SCHEMA,
|
|
5091
|
+
...receivingPublication(args.config, args.dryRun),
|
|
5011
5092
|
runId: args.runId,
|
|
5012
5093
|
mode: args.dryRun ? "dry-run" : "live",
|
|
5013
5094
|
simCount: specs.length,
|