humanish 0.83.1 → 0.84.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/run.js CHANGED
@@ -3605,12 +3605,29 @@ async function missingLocalEvidenceArtifacts(runPaths, bundle) {
3605
3605
  if (stream.ui?.nestedObserverPath && isLocalEvidenceArtifactPath(stream.ui.nestedObserverPath)) {
3606
3606
  addRequiredPath(stream.ui.nestedObserverPath);
3607
3607
  }
3608
+ for (const reference of declaredActorScreenshotReferences(stream)) {
3609
+ if (isRunRootEvidenceReference(reference.path)) {
3610
+ addRequiredPath(reference.path, { screenshot: true });
3611
+ }
3612
+ }
3608
3613
  }
3609
3614
  for (const artifact of bundle.adapterArtifacts ?? []) {
3610
3615
  if (isLocalEvidenceArtifactPath(artifact.path)) {
3611
3616
  addRequiredPath(artifact.path, { screenshot: artifact.kind === "screenshot" });
3612
3617
  }
3613
3618
  }
3619
+ for (const candidate of bundle.feedbackCandidates ?? []) {
3620
+ for (const evidence of candidate.evidence) {
3621
+ if (isRunRootEvidenceReference(evidence.path)) {
3622
+ addRequiredPath(evidence.path, {
3623
+ screenshot: evidence.kind === "screenshot",
3624
+ // Feedback accepts an existing empty nonimage file. The conjunctive merge above
3625
+ // keeps any stricter stream, actor, or adapter requirement in force.
3626
+ allowEmpty: evidence.kind !== "screenshot"
3627
+ });
3628
+ }
3629
+ }
3630
+ }
3614
3631
  const missing = [];
3615
3632
  for (const [artifactPath, requirements] of requiredPaths) {
3616
3633
  const bytes = await readSafeRunArtifactBytes(runPaths, artifactPath);
@@ -3632,6 +3649,29 @@ function isZeroEventTerminalTrace(value) {
3632
3649
  && value.protocol === "terminal-exec" && value.lane === "terminal"
3633
3650
  && isRecord(value.counts) && value.counts.terminalEvents === 0;
3634
3651
  }
3652
+ function declaredActorScreenshotReferences(stream) {
3653
+ const references = [];
3654
+ for (const field of ["actor", "liveActor"]) {
3655
+ const trace = stream[field];
3656
+ if (!isRecord(trace) || !Array.isArray(trace.items))
3657
+ continue;
3658
+ trace.items.forEach((item, index) => {
3659
+ if (!isRecord(item) || !Object.hasOwn(item, "screenshotRef"))
3660
+ return;
3661
+ references.push({
3662
+ label: `${stream.id} ${field}.items[${index}].screenshotRef`,
3663
+ path: isRecord(item.screenshotRef) ? item.screenshotRef.path : undefined,
3664
+ redaction: isRecord(item.screenshotRef) ? item.screenshotRef.redaction : undefined
3665
+ });
3666
+ });
3667
+ }
3668
+ return references;
3669
+ }
3670
+ function isRunRootEvidenceReference(value) {
3671
+ return typeof value === "string" && isLocalEvidenceArtifactPath(value)
3672
+ && !path.win32.isAbsolute(value) && !value.includes("\0")
3673
+ && !/^[a-z][a-z\d+.-]*:/i.test(value);
3674
+ }
3635
3675
  function invalidRunEvidenceReferences(bundle) {
3636
3676
  const findings = [];
3637
3677
  if (path.isAbsolute(bundle.cwd)) {
@@ -3648,6 +3688,13 @@ function invalidRunEvidenceReferences(bundle) {
3648
3688
  findings.push(`adapter artifact ${artifact.namespace}:${artifact.kind} nonlocal artifact ${artifact.path}`);
3649
3689
  }
3650
3690
  }
3691
+ for (const candidate of bundle.feedbackCandidates ?? []) {
3692
+ for (const evidence of candidate.evidence) {
3693
+ if (!isRunRootEvidenceReference(evidence.path)) {
3694
+ findings.push(`feedback candidate ${candidate.id} nonlocal evidence ${evidence.path}`);
3695
+ }
3696
+ }
3697
+ }
3651
3698
  for (const stream of bundle.streams) {
3652
3699
  const seen = new Set();
3653
3700
  for (const artifact of stream.artifacts) {
@@ -3669,6 +3716,11 @@ function invalidRunEvidenceReferences(bundle) {
3669
3716
  if (stream.ui?.screenshotUrl && !normalizeLocalEvidenceReference(stream.ui.screenshotUrl)) {
3670
3717
  findings.push(`${stream.id} nonlocal screenshot reference ${stream.ui.screenshotUrl}`);
3671
3718
  }
3719
+ for (const reference of declaredActorScreenshotReferences(stream)) {
3720
+ if (!isRunRootEvidenceReference(reference.path)) {
3721
+ findings.push(`${reference.label} is malformed or nonlocal`);
3722
+ }
3723
+ }
3672
3724
  }
3673
3725
  return findings.slice(0, 50);
3674
3726
  }
@@ -3967,7 +4019,11 @@ function rawScreenshotStreamIds(bundle) {
3967
4019
  const rawStreamIds = [];
3968
4020
  for (const stream of bundle.streams) {
3969
4021
  const trace = stream.actor;
3970
- if (isRecord(trace) && isRecord(trace.redaction) && trace.redaction.screenshots === "raw") {
4022
+ const aggregateRaw = isRecord(trace) && isRecord(trace.redaction) && trace.redaction.screenshots === "raw";
4023
+ // Partial live traces have no final actor summary. An explicit raw frame must also
4024
+ // retain local-only posture, including when it contradicts an aggregate blur claim.
4025
+ const frameRaw = declaredActorScreenshotReferences(stream).some((reference) => reference.redaction === "none");
4026
+ if (aggregateRaw || frameRaw) {
3971
4027
  rawStreamIds.push(stream.id);
3972
4028
  }
3973
4029
  }