humanish 0.80.0 → 0.81.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 +9 -3
- package/dist/actor-contract.d.ts +16 -0
- package/dist/actor-contract.js.map +1 -1
- package/dist/chrome-cdp-probe.js +13 -0
- package/dist/chrome-cdp-probe.js.map +1 -1
- package/dist/computer-use.d.ts +7 -1
- package/dist/computer-use.js +156 -13
- package/dist/computer-use.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +7 -6
- package/dist/cua-actor-lab.js +95 -21
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/e2b-desktop-launch.d.ts +28 -1
- package/dist/e2b-desktop-launch.js +88 -1
- package/dist/e2b-desktop-launch.js.map +1 -1
- package/dist/e2b-desktop-screenshot-cleanup.d.ts +15 -0
- package/dist/e2b-desktop-screenshot-cleanup.js +67 -0
- package/dist/e2b-desktop-screenshot-cleanup.js.map +1 -0
- package/dist/e2b-terminal-lab.d.ts +3 -2
- package/dist/e2b-terminal-lab.js +176 -85
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/first-run-path.js +2 -2
- package/dist/first-run-path.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lab-config.d.ts +11 -14
- package/dist/lab-config.js +2 -2
- package/dist/lab-config.js.map +1 -1
- package/dist/openai-responses-cu.js +76 -49
- package/dist/openai-responses-cu.js.map +1 -1
- package/dist/program.js +16 -7
- package/dist/program.js.map +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/terminal-node-bootstrap.d.ts +4 -0
- package/dist/terminal-node-bootstrap.js +58 -0
- package/dist/terminal-node-bootstrap.js.map +1 -0
- package/dist/terminal-runtime-auth.d.ts +13 -0
- package/dist/terminal-runtime-auth.js +24 -0
- package/dist/terminal-runtime-auth.js.map +1 -0
- package/docs/architecture/actor-contract.md +22 -0
- package/docs/architecture/terminal-product-lane.md +108 -7
- package/docs/contracts/feedback.md +14 -0
- package/docs/contracts/schemas.md +1 -1
- package/docs/goals/current.md +45 -13
- package/docs/ramp/README.md +1 -1
- package/package.json +4 -2
package/dist/cua-actor-lab.d.ts
CHANGED
|
@@ -793,10 +793,11 @@ export declare function captureDesktopBrowserGeometry(args: {
|
|
|
793
793
|
*/
|
|
794
794
|
export declare function resolveSelfReportedBlocker(session: CuaLoopResult | undefined): string | undefined;
|
|
795
795
|
/**
|
|
796
|
-
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
* quoted
|
|
796
|
+
* Friction is independent of how a completed session ended (#657). Read the participant's
|
|
797
|
+
* redacted messages, including earlier reports, rather than the harness-owned reason that
|
|
798
|
+
* stopWhen/dwell writes. Reasoning, observations, and notices are not participant reports.
|
|
799
|
+
* Resolved arcs still count (#453); quoted copy and negated reports still do not. This read
|
|
800
|
+
* never changes the verdict. Exported for testing.
|
|
800
801
|
*/
|
|
801
802
|
export declare function resolveSelfReportedFriction(session: CuaLoopResult | undefined): string | undefined;
|
|
802
803
|
/**
|
|
@@ -939,8 +940,8 @@ export declare function buildCuaCostSummary(args: {
|
|
|
939
940
|
* trying. A clean pass files nothing here — feedback exists to carry findings, and a run without
|
|
940
941
|
* any falls back to an honest live summary in the draft layer instead of a template.
|
|
941
942
|
*
|
|
942
|
-
* Everything quoted is already scrub+redacted — `session.reason`
|
|
943
|
-
*
|
|
943
|
+
* Everything quoted is already scrub+redacted — participant messages and `session.reason` pass
|
|
944
|
+
* through redactNarration in the loop — and passes redactText again here as defense-in-depth.
|
|
944
945
|
*/
|
|
945
946
|
export declare function participantFeedbackCandidates(args: {
|
|
946
947
|
runId: string;
|
package/dist/cua-actor-lab.js
CHANGED
|
@@ -1063,7 +1063,11 @@ export async function applyMobileEmulation(desktop, requestTimeoutMs, endpoint,
|
|
|
1063
1063
|
throw new Error(`mobile emulation could not be applied (${announced.unavailable}); applied before failing: ${(announced.applied ?? []).join(", ") || "nothing"}`);
|
|
1064
1064
|
}
|
|
1065
1065
|
const applied = announced;
|
|
1066
|
-
|
|
1066
|
+
// Viewport/touch read-back proves context settings, not gesture equivalence. Two hosted
|
|
1067
|
+
// replicas and a native-X conversion-toggle control reproduced reset click counts (#676).
|
|
1068
|
+
const warnings = request.touch
|
|
1069
|
+
? ["Mobile emulation uses desktop pointer-to-touch conversion, which can differ for repeated taps. Confirm gesture failures with direct or native touch input before attributing them to the app."]
|
|
1070
|
+
: [];
|
|
1067
1071
|
// The reload inside the applier takes a moment; the read-back is retried until the page reports
|
|
1068
1072
|
// the requested viewport and user agent, so a slow page does not read as "no proof".
|
|
1069
1073
|
let readBack = await read();
|
|
@@ -1335,14 +1339,56 @@ const REPORTED_DEFECT_LANGUAGE = /\b(defects?|bugs?|accessibilit(y|ies)|inaccess
|
|
|
1335
1339
|
// counted as reported friction and became a feedback candidate whose "actual" was a sentence
|
|
1336
1340
|
// reporting no problem. Only the report-shaped adjectives are negatable here: "no visible focus",
|
|
1337
1341
|
// "not keyboard-accessible" and "did nothing" are defects and stay.
|
|
1338
|
-
const
|
|
1342
|
+
const NEGATED_REPORT_ITEM = String.raw `(?:confus(?:ed|ing|ion)|unclear(?:\s+error\s+output)?|unexpected(?:ly)?|hesitat(?:ed|ion|ions)|surpris(?:ed|ing|es)|defects?|bugs?|overlap(?:ped|ping|s)?|truncat(?:ed|es|ion)|hard to (?:find|tell|see|read|reach)|blockers?|blocking issues?|errors?(?:\s+output)?|failures?|problems?|issues?)`;
|
|
1343
|
+
const NEGATED_REPORT_QUALIFIERS = String.raw `(?:(?:really|particularly|especially|major|minor|real|actual|remaining|functional|obvious|noticeable|significant|any|a|an)\s+)*`;
|
|
1344
|
+
const NEGATED_REPORT_MODIFIERS = String.raw `(?:(?:was|were|felt|seemed|really|particularly|especially|major|minor|real|actual|remaining|functional|obvious|noticeable|significant|any|a|an|encounter(?:ed)?|experience(?:d)?|notice(?:d)?|observe(?:d)?|feel|find|found|have|had)\s+)*`;
|
|
1345
|
+
const NEGATED_REPORT_LANGUAGE = new RegExp(String.raw `\b(?:nothing|no|not|never|without|none|(?:did|do|does|was|were|has|have|had)n['’]t)\s+${NEGATED_REPORT_MODIFIERS}${NEGATED_REPORT_ITEM}\b`
|
|
1346
|
+
// Negation scopes over a coordinated report list, not the rest of the sentence. In
|
|
1347
|
+
// particular, leave "but the label was confusing" and "and Save did nothing" intact.
|
|
1348
|
+
+ String.raw `(?:\s*,?\s+(?:or|nor|and)\s+${NEGATED_REPORT_QUALIFIERS}${NEGATED_REPORT_ITEM}\b)*`
|
|
1349
|
+
// Keep the predicate inside its negation: "No errors blocked me" reports no blocker.
|
|
1350
|
+
+ String.raw `(?:\s+(?:blocked|stopped|prevented)\s+(?:me|us|it)\b)?`, "g");
|
|
1339
1351
|
function stripNegatedReportLanguage(text) {
|
|
1340
|
-
|
|
1352
|
+
// "Not without hesitation" reports hesitation; do not let the inner "without" erase it.
|
|
1353
|
+
return text.replace(/\b(?:not|never)\s+without\b/g, "with").replace(NEGATED_REPORT_LANGUAGE, " ");
|
|
1341
1354
|
}
|
|
1342
1355
|
function completionReasonContradictsGoal(reason) {
|
|
1343
|
-
|
|
1356
|
+
// Preserve the full negated list before the older blocker-specific rules remove its first
|
|
1357
|
+
// noun ("no issues or hesitation"). Matching reports first also avoids their broad encounter
|
|
1358
|
+
// clause rule swallowing a genuine subsequent observation.
|
|
1359
|
+
const text = stripQuotedSpans(stripNegatedNonBlockerPhrases(stripNegatedReportLanguage(stripCodeExamples(reason).toLowerCase())));
|
|
1344
1360
|
return hasBlockerLanguage(text) || REPORTED_DEFECT_LANGUAGE.test(text);
|
|
1345
1361
|
}
|
|
1362
|
+
/** Code/documentation excerpts are quoted material, not participant observations. */
|
|
1363
|
+
function stripCodeExamples(text) {
|
|
1364
|
+
return text
|
|
1365
|
+
// Include an unterminated fence: copied text is not promoted just because its closing fence
|
|
1366
|
+
// was omitted. Match the same marker so backticks inside a tilde fence cannot end it early.
|
|
1367
|
+
.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[ \t]*$|(?![\s\S]))/gm, " ")
|
|
1368
|
+
.replace(/(`+)[^`\n]*\1/g, " ");
|
|
1369
|
+
}
|
|
1370
|
+
/** Interim messages also contain plans and hypotheses. Admit observed-report clauses only;
|
|
1371
|
+
* the established closing-report scan remains separate. This is a conservative text heuristic,
|
|
1372
|
+
* not an assertion that every mention of a defect is evidence that one happened. */
|
|
1373
|
+
function interimMessageReportsFriction(message) {
|
|
1374
|
+
const prose = stripQuotedSpans(stripCodeExamples(message).toLowerCase());
|
|
1375
|
+
const clauses = prose.split(/(?<=[.!?])\s+|[;\n]+|,\s*(?:but|so|however|yet)\s+|\s+so\s+(?=i\b|we\b)/);
|
|
1376
|
+
return clauses.some((clause) => {
|
|
1377
|
+
// A condition, question, intention, or conjecture does not assert an observed result.
|
|
1378
|
+
// Clause splitting above keeps "Save did nothing, so I will try Enter" observable.
|
|
1379
|
+
if (/\?|\b(?:if|unless|whether|maybe|perhaps|suppose|hypothetically|might|may|would|should)\b|\bcould\b(?!\s+not\b)/.test(clause)
|
|
1380
|
+
|| /\b(?:i|we)(?:['’]ll|\s+(?:will|plan|intend|want|hope|suspect|wonder))\b|\bgoing to\b|\blet['’]s\b/.test(clause)
|
|
1381
|
+
|| /\b(?:task|goal|mission|objective|plan)\s+(?:(?:is|was)\s+)?to\b|^\s*(?:check|test|look|checking|testing)\b/.test(clause))
|
|
1382
|
+
return false;
|
|
1383
|
+
// A topic is not a defect ("the accessibility guide is open", "shows error-handling docs").
|
|
1384
|
+
// Actual friction in those surfaces still qualifies: "the error guide was confusing".
|
|
1385
|
+
const observation = clause
|
|
1386
|
+
.replace(/\baccessibilit(?:y|ies)\b/g, " ")
|
|
1387
|
+
.replace(/\berror[- ]handling\b|\berror\s+(?:documentation|docs?|guides?|reference|examples?)\b/g, " ");
|
|
1388
|
+
const assertsObservation = /\b(?:is|are|was|were|has|had|did|does|shows?|showed|seems?|seemed|looks?|looked|found|noticed|saw|hit|encountered|felt|got|failed|returned|cannot|can['’]?t|unable|could not)\b|\b(?:overlap(?:ped|ping|s)?|truncat(?:ed|es)|cut off|nothing happened|no (?:visible )?focus)\b/.test(observation);
|
|
1389
|
+
return assertsObservation && completionReasonContradictsGoal(observation);
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1346
1392
|
/** The verdict scan (strict): like the friction scan, but resolved-arc segments are stripped
|
|
1347
1393
|
* first — failure narration the participant itself reports as overcome is friction on the
|
|
1348
1394
|
* road, not a blocker at the destination (#453). */
|
|
@@ -1438,21 +1484,39 @@ export function resolveSelfReportedBlocker(session) {
|
|
|
1438
1484
|
: undefined;
|
|
1439
1485
|
}
|
|
1440
1486
|
/**
|
|
1441
|
-
*
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1444
|
-
* quoted
|
|
1487
|
+
* Friction is independent of how a completed session ended (#657). Read the participant's
|
|
1488
|
+
* redacted messages, including earlier reports, rather than the harness-owned reason that
|
|
1489
|
+
* stopWhen/dwell writes. Reasoning, observations, and notices are not participant reports.
|
|
1490
|
+
* Resolved arcs still count (#453); quoted copy and negated reports still do not. This read
|
|
1491
|
+
* never changes the verdict. Exported for testing.
|
|
1445
1492
|
*/
|
|
1446
1493
|
export function resolveSelfReportedFriction(session) {
|
|
1494
|
+
if (session?.completionReason !== "goal_satisfied")
|
|
1495
|
+
return undefined;
|
|
1447
1496
|
// Friction stays a read of the narrative even when the outcome was declared: a participant who
|
|
1448
1497
|
// reached the goal and described what was hard on the way has reported friction.
|
|
1449
|
-
if (session
|
|
1498
|
+
if (session.trace.declaredOutcome === "blocked")
|
|
1450
1499
|
return session.reason;
|
|
1451
|
-
|
|
1452
|
-
&&
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1500
|
+
const messages = session.trace.items
|
|
1501
|
+
.filter((item) => item.kind === "message" && item.id !== session.trace.debrief?.messageId)
|
|
1502
|
+
.map((item) => item.text?.trim() ?? "")
|
|
1503
|
+
.filter((text) => text.length > 0);
|
|
1504
|
+
// A custom session may keep its closing report only in reason even when earlier messages exist.
|
|
1505
|
+
// Structured stop/dwell reasons are controller text and never enter this closing-report path.
|
|
1506
|
+
const closingReport = traceHasStopWhenMatch(session) ? undefined : session.reason.trim();
|
|
1507
|
+
// One candidate per participant, with exact repeats removed (the closing report often repeats
|
|
1508
|
+
// a prior turn). Earlier turns require observed-report clauses, not arbitrary defect mentions.
|
|
1509
|
+
const typedReports = session.trace.debrief?.status === "completed"
|
|
1510
|
+
? session.trace.debrief.report?.frictionReports ?? [] : [];
|
|
1511
|
+
const reports = [...new Set([...typedReports, ...messages.filter((message) => message === closingReport
|
|
1512
|
+
? completionReasonContradictsGoal(message)
|
|
1513
|
+
: interimMessageReportsFriction(message))])];
|
|
1514
|
+
if (closingReport && completionReasonContradictsGoal(closingReport) && !reports.includes(closingReport)) {
|
|
1515
|
+
reports.push(closingReport);
|
|
1516
|
+
}
|
|
1517
|
+
if (reports.length > 0)
|
|
1518
|
+
return reports.join("\n\n");
|
|
1519
|
+
return undefined;
|
|
1456
1520
|
}
|
|
1457
1521
|
/**
|
|
1458
1522
|
* Run ONE E2B desktop lane end-to-end: create the sandbox (per-lane metadata + the lane's device
|
|
@@ -1607,13 +1671,11 @@ export async function runCuaLane(spec, deps) {
|
|
|
1607
1671
|
dpi: 96,
|
|
1608
1672
|
lifecycle: { onTimeout: "kill" }
|
|
1609
1673
|
}, config.execution?.desktop?.template, {
|
|
1610
|
-
//
|
|
1611
|
-
//
|
|
1612
|
-
// never learned the id of; its own timeoutMs is what reclaims it, so the warning says so.
|
|
1674
|
+
// The default loader reclaims an acquired handle before retrying failed desktop startup.
|
|
1675
|
+
// Its error names the cleanup outcome; pre-construction allocation failures remain unowned.
|
|
1613
1676
|
onRetry: (reason) => {
|
|
1614
1677
|
const named = redactText(deps.scrubKnownValues(reason));
|
|
1615
|
-
warnings.push(`Sandbox create for lane ${spec.laneId} retried once after a transient provider error (${named});
|
|
1616
|
-
`a sandbox the first attempt may have allocated is not known to this run and expires on the provider's ${Math.round(deps.perLaneSandboxMs / 60_000)}-minute timeout.`);
|
|
1678
|
+
warnings.push(`Sandbox create for lane ${spec.laneId} retried once after a transient provider error (${named}).`);
|
|
1617
1679
|
onSubjectPhase({ at: new Date(deps.now()).toISOString(), type: "cua-lab.sandbox.create.retry", message: `sandbox create retried once (${named})` });
|
|
1618
1680
|
}
|
|
1619
1681
|
});
|
|
@@ -3849,6 +3911,18 @@ export function buildCuaCostSummary(args) {
|
|
|
3849
3911
|
sumInput += usage.input ?? 0;
|
|
3850
3912
|
sumOutput += usage.output ?? 0;
|
|
3851
3913
|
}
|
|
3914
|
+
// An attempted closing request can fail after provider work without reporting usage.
|
|
3915
|
+
// Keep the known interaction estimate and make the additional unknown explicit.
|
|
3916
|
+
if (lane.trace.debrief?.usageReported === false) {
|
|
3917
|
+
breakdown.push({
|
|
3918
|
+
kind: "model-tokens",
|
|
3919
|
+
...(lane.laneId === undefined ? {} : { laneId: lane.laneId }),
|
|
3920
|
+
...(lane.trace.providerVersion === undefined ? {} : { modelId: lane.trace.providerVersion }),
|
|
3921
|
+
estimatedCostUsd: null,
|
|
3922
|
+
reason: "closing_usage_unreported",
|
|
3923
|
+
ratesAsOf: null
|
|
3924
|
+
});
|
|
3925
|
+
}
|
|
3852
3926
|
const est = lane.trace.estimatedCost;
|
|
3853
3927
|
if (!est) {
|
|
3854
3928
|
continue;
|
|
@@ -3929,8 +4003,8 @@ function desktopSpanToMinutes(desktopDurationMs) {
|
|
|
3929
4003
|
* trying. A clean pass files nothing here — feedback exists to carry findings, and a run without
|
|
3930
4004
|
* any falls back to an honest live summary in the draft layer instead of a template.
|
|
3931
4005
|
*
|
|
3932
|
-
* Everything quoted is already scrub+redacted — `session.reason`
|
|
3933
|
-
*
|
|
4006
|
+
* Everything quoted is already scrub+redacted — participant messages and `session.reason` pass
|
|
4007
|
+
* through redactNarration in the loop — and passes redactText again here as defense-in-depth.
|
|
3934
4008
|
*/
|
|
3935
4009
|
export function participantFeedbackCandidates(args) {
|
|
3936
4010
|
const candidates = [];
|