humanish 0.49.0 → 0.51.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.
Files changed (65) hide show
  1. package/README.md +52 -3
  2. package/dist/concurrent-shared-world-lab.d.ts +5 -0
  3. package/dist/concurrent-shared-world-lab.js +28 -0
  4. package/dist/concurrent-shared-world-lab.js.map +1 -1
  5. package/dist/cua-actor-lab.d.ts +7 -0
  6. package/dist/cua-actor-lab.js +64 -3
  7. package/dist/cua-actor-lab.js.map +1 -1
  8. package/dist/e2b-terminal-lab.d.ts +7 -0
  9. package/dist/e2b-terminal-lab.js +54 -0
  10. package/dist/e2b-terminal-lab.js.map +1 -1
  11. package/dist/init-templates.js +7 -2
  12. package/dist/init-templates.js.map +1 -1
  13. package/dist/lab-engine.d.ts +11 -0
  14. package/dist/lab-engine.js +18 -0
  15. package/dist/lab-engine.js.map +1 -1
  16. package/dist/lab-summary.d.ts +36 -0
  17. package/dist/lab-summary.js +86 -0
  18. package/dist/lab-summary.js.map +1 -0
  19. package/dist/oss-lab.d.ts +3 -0
  20. package/dist/oss-lab.js.map +1 -1
  21. package/dist/oss-meta-lab.d.ts +3 -0
  22. package/dist/oss-meta-lab.js +13 -0
  23. package/dist/oss-meta-lab.js.map +1 -1
  24. package/dist/program.d.ts +14 -0
  25. package/dist/program.js +131 -9
  26. package/dist/program.js.map +1 -1
  27. package/dist/run-detail.d.ts +59 -0
  28. package/dist/run-detail.js +108 -0
  29. package/dist/run-detail.js.map +1 -0
  30. package/dist/run-index.d.ts +71 -0
  31. package/dist/run-index.js +206 -0
  32. package/dist/run-index.js.map +1 -0
  33. package/dist/run-paths.js +16 -1
  34. package/dist/run-paths.js.map +1 -1
  35. package/dist/run-projection.d.ts +182 -0
  36. package/dist/run-projection.js +349 -0
  37. package/dist/run-projection.js.map +1 -0
  38. package/dist/run-status.d.ts +139 -0
  39. package/dist/run-status.js +218 -0
  40. package/dist/run-status.js.map +1 -0
  41. package/dist/run.d.ts +15 -0
  42. package/dist/run.js +91 -6
  43. package/dist/run.js.map +1 -1
  44. package/dist/scripted-browser-lab.d.ts +5 -0
  45. package/dist/scripted-browser-lab.js +27 -0
  46. package/dist/scripted-browser-lab.js.map +1 -1
  47. package/dist/shared-world-lab.d.ts +5 -0
  48. package/dist/shared-world-lab.js +27 -0
  49. package/dist/shared-world-lab.js.map +1 -1
  50. package/dist/tui-app.js +402 -0
  51. package/dist/tui-contract.d.ts +84 -0
  52. package/dist/tui-contract.js +32 -0
  53. package/dist/tui-contract.js.map +1 -0
  54. package/dist/tui-launch.d.ts +48 -0
  55. package/dist/tui-launch.js +159 -0
  56. package/dist/tui-launch.js.map +1 -0
  57. package/dist/tui-project.d.ts +9 -0
  58. package/dist/tui-project.js +18 -0
  59. package/dist/tui-project.js.map +1 -0
  60. package/docs/contracts/run-bundle.md +8 -0
  61. package/docs/contracts/schemas.md +70 -1
  62. package/docs/goals/current.md +3 -2
  63. package/docs/ramp/README.md +1 -1
  64. package/package.json +8 -5
  65. package/skills/humanish/SKILL.md +19 -0
@@ -26,6 +26,7 @@ import { readFile, realpath, rm } from "node:fs/promises";
26
26
  import path from "node:path";
27
27
  import { runDesktopCommandOrThrow, toErrorMessage } from "./command-failure.js";
28
28
  import { pathToFileURL } from "node:url";
29
+ import { beginRunStatus } from "./run-status.js";
29
30
  import { adapterScoreFailureMessage, applyBrowserAdapterHooks } from "./adapter-extension.js";
30
31
  import { actorRegistry, isCuaActorDescriptor } from "./actor-registry.js";
31
32
  import { CHROMIUM_EVIDENCE_HYGIENE_FLAGS, chromiumEvidenceProfilePreferencesJson } from "./browser-evidence-hygiene.js";
@@ -2261,6 +2262,17 @@ export async function runCuaActorLab(options) {
2261
2262
  }
2262
2263
  const runId = options.runId ?? makeCuaRunId();
2263
2264
  const runPaths = await prepareRunArtifactPaths(cwd, runId);
2265
+ // Identity + liveness on disk from the first moment (#455): anything watching the runs
2266
+ // directory — the TUI, another terminal, an agent — can now tell which lab this is and that
2267
+ // it is alive, without waiting for the interactive observer flush that used to be the only
2268
+ // mid-run write. The success path finalizes it with the real outcome; the fail-closed returns
2269
+ // below do not, so `runLab`'s status scope finalizes those with no outcome. A crash reaches
2270
+ // neither and leaves the record stale, which reads as interrupted rather than as a lie.
2271
+ const runStatus = beginRunStatus(runPaths, {
2272
+ runId,
2273
+ mode: dryRun ? "dry-run" : "live",
2274
+ ...(options.lab === undefined ? {} : { lab: options.lab })
2275
+ });
2264
2276
  const artifactRoot = runPaths.absoluteRunRoot;
2265
2277
  const physicalArtifactRoot = runPaths.physicalRunRoot;
2266
2278
  const createdAt = new Date().toISOString();
@@ -2353,9 +2365,16 @@ export async function runCuaActorLab(options) {
2353
2365
  }));
2354
2366
  const inProgressAggregateSubject = inProgressLaneSubjects[0];
2355
2367
  const inProgressProvenance = subjectProvenanceArg(inProgressAggregateSubject, publicRepo, subjectEnvNames);
2356
- if (!dryRun && options.onObserverReady) {
2368
+ // A live run writes what it is doing AS IT DOES IT, whether or not anyone is currently watching.
2369
+ // This used to be gated on `options.onObserverReady` — the interactive Observer callback — so a
2370
+ // run launched by an agent (`lab run --json`), detached, or from the terminal surface recorded
2371
+ // nothing at all until it completed, and anything asking "what is this participant doing right
2372
+ // now" got silence for the whole run. Who reads the evidence is not the run's business; the
2373
+ // callback below stays conditional, the writing does not.
2374
+ if (!dryRun) {
2357
2375
  const inProgressBundle = laneCount === 1 && rerunLineage === undefined
2358
2376
  ? buildSingleLaneBundle({
2377
+ ...(options.lab === undefined ? {} : { lab: options.lab }),
2359
2378
  spec: laneSpecs[0],
2360
2379
  outcome: undefined,
2361
2380
  descriptor,
@@ -2372,6 +2391,7 @@ export async function runCuaActorLab(options) {
2372
2391
  localAppSubject
2373
2392
  })
2374
2393
  : buildCuaFanoutBundle({
2394
+ ...(options.lab === undefined ? {} : { lab: options.lab }),
2375
2395
  specs: laneSpecs,
2376
2396
  laneSubjects: inProgressLaneSubjects,
2377
2397
  aggregateSubject: inProgressAggregateSubject,
@@ -2394,7 +2414,8 @@ export async function runCuaActorLab(options) {
2394
2414
  liveObserver = observerResultForCuaArtifacts(cwd, runId, artifactRoot, [
2395
2415
  "Live CUA Observer is attached before final verification; stream auth URLs are runtime-only and are not persisted."
2396
2416
  ]);
2397
- await options.onObserverReady(liveObserver);
2417
+ if (options.onObserverReady)
2418
+ await options.onObserverReady(liveObserver);
2398
2419
  // Incremental live flush (#441): as each lane's loop reports its recorded-so-far items,
2399
2420
  // rewrite the in-progress bundle with per-stream `liveActor` partials so the attached
2400
2421
  // Observer's 5s poll sees the timeline grow. Throttled (one write per interval, trailing
@@ -2402,6 +2423,10 @@ export async function runCuaActorLab(options) {
2402
2423
  // write so a stale flush can never resurrect the in-progress bundle. A flush failure is
2403
2424
  // swallowed: mid-run observability must never break the run itself.
2404
2425
  const streamIdByLane = new Map(laneSpecs.map((spec) => [spec.laneId, spec.streamId]));
2426
+ // The persona each lane is running, so the live flush can say who is in it.
2427
+ const personaByStream = new Map(laneSpecs
2428
+ .map((spec) => [spec.streamId, spec.persona?.id])
2429
+ .filter((entry) => typeof entry[1] === "string"));
2405
2430
  const liveItemsByStream = new Map();
2406
2431
  let flushWriting;
2407
2432
  let flushDirty = false;
@@ -2420,7 +2445,20 @@ export async function runCuaActorLab(options) {
2420
2445
  const liveItems = liveItemsByStream.get(stream.id);
2421
2446
  return liveItems === undefined
2422
2447
  ? stream
2423
- : { ...stream, liveActor: { schema: "humanish.live-actor.v1", updatedAt, items: [...liveItems] } };
2448
+ : {
2449
+ ...stream,
2450
+ liveActor: {
2451
+ schema: "humanish.live-actor.v1",
2452
+ updatedAt,
2453
+ // WHO is in this lane, carried while the run is live. Without it a surface
2454
+ // watching a live run can only name the lane, and "CUA browser — observer-live-
2455
+ // check" is the harness talking about itself where the participant should be.
2456
+ ...(personaByStream.get(stream.id) === undefined
2457
+ ? {}
2458
+ : { persona: { id: personaByStream.get(stream.id) } }),
2459
+ items: [...liveItems]
2460
+ }
2461
+ };
2424
2462
  })
2425
2463
  };
2426
2464
  try {
@@ -2585,6 +2623,7 @@ export async function runCuaActorLab(options) {
2585
2623
  const finalProvenance = subjectProvenanceArg(aggregateSubject, publicRepo, subjectEnvNames);
2586
2624
  const bundle = laneCount === 1 && rerunLineage === undefined
2587
2625
  ? buildSingleLaneBundle({
2626
+ ...(options.lab === undefined ? {} : { lab: options.lab }),
2588
2627
  spec: laneSpecs[0],
2589
2628
  outcome: outcomes?.[0],
2590
2629
  descriptor,
@@ -2600,6 +2639,7 @@ export async function runCuaActorLab(options) {
2600
2639
  localAppSubject
2601
2640
  })
2602
2641
  : buildCuaFanoutBundle({
2642
+ ...(options.lab === undefined ? {} : { lab: options.lab }),
2603
2643
  specs: laneSpecs,
2604
2644
  ...(outcomes === undefined ? {} : { outcomes }),
2605
2645
  laneSubjects,
@@ -2639,6 +2679,24 @@ export async function runCuaActorLab(options) {
2639
2679
  ...(options.scorerProvenance === undefined ? {} : { scorerProvenance: options.scorerProvenance })
2640
2680
  });
2641
2681
  await writeCuaRunArtifacts(bundle, createdAt, runPaths);
2682
+ // Finalize the status record from the bundle that was just written, so the index can never
2683
+ // claim an outcome the evidence does not carry. A run that throws before reaching here leaves
2684
+ // its record `running` and goes stale — read as interrupted, which is the truth.
2685
+ await runStatus.finish({
2686
+ ...(bundle.review?.verdict === undefined ? {} : { verdict: bundle.review.verdict }),
2687
+ ...(bundle.review?.participants === undefined
2688
+ ? {}
2689
+ : {
2690
+ participants: {
2691
+ total: bundle.review.participants.total,
2692
+ reachedGoal: bundle.review.participants.reachedGoal,
2693
+ ...(bundle.review.participants.reportedFriction === undefined
2694
+ ? {}
2695
+ : { reportedFriction: bundle.review.participants.reportedFriction })
2696
+ }
2697
+ }),
2698
+ ...(bundle.cost?.estimatedTotalUsd === undefined ? {} : { estimatedCostUsd: bundle.cost.estimatedTotalUsd })
2699
+ });
2642
2700
  const observer = await render(cwd, runId, { open: options.open === true });
2643
2701
  if (observer.ok && runtimeStreamUrls.length > 0) {
2644
2702
  attachObserverRuntimeStreamUrls(observer, runtimeStreamUrls);
@@ -2801,6 +2859,7 @@ function observerResultForCuaArtifacts(cwd, runId, artifactRoot, warnings = [])
2801
2859
  function buildSingleLaneBundle(args) {
2802
2860
  const { spec, outcome, config } = args;
2803
2861
  return buildCuaBundle({
2862
+ ...(args.lab === undefined ? {} : { lab: args.lab }),
2804
2863
  actorId: args.descriptor.id,
2805
2864
  appUrl: args.appUrl,
2806
2865
  laneId: spec.laneId,
@@ -3606,6 +3665,7 @@ export function buildCuaBundle(args) {
3606
3665
  createdAt: args.createdAt,
3607
3666
  cwd: PUBLIC_TARGET_CWD,
3608
3667
  artifactRoot: path.join(".humanish", "runs", args.runId),
3668
+ ...(args.lab === undefined ? {} : { lab: args.lab }),
3609
3669
  source: args.source,
3610
3670
  persona: {
3611
3671
  id: args.persona.id,
@@ -4078,6 +4138,7 @@ export function buildCuaFanoutBundle(args) {
4078
4138
  createdAt: args.createdAt,
4079
4139
  cwd: PUBLIC_TARGET_CWD,
4080
4140
  artifactRoot: path.join(".humanish", "runs", args.runId),
4141
+ ...(args.lab === undefined ? {} : { lab: args.lab }),
4081
4142
  source: args.source,
4082
4143
  persona: {
4083
4144
  id: specs[0].persona.id,