humanish 0.87.0 → 0.88.1

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 (55) hide show
  1. package/README.md +12 -2
  2. package/dist/actor-contract.d.ts +4 -1
  3. package/dist/actor-contract.js.map +1 -1
  4. package/dist/actor-goal-source.d.ts +5 -0
  5. package/dist/actor-goal-source.js +32 -0
  6. package/dist/actor-goal-source.js.map +1 -0
  7. package/dist/actor-stop-cause.d.ts +2 -0
  8. package/dist/actor-stop-cause.js +7 -2
  9. package/dist/actor-stop-cause.js.map +1 -1
  10. package/dist/computer-use.d.ts +3 -0
  11. package/dist/computer-use.js +30 -4
  12. package/dist/computer-use.js.map +1 -1
  13. package/dist/cua-actor-lab.d.ts +10 -3
  14. package/dist/cua-actor-lab.js +35 -10
  15. package/dist/cua-actor-lab.js.map +1 -1
  16. package/dist/cua-admission-limit.d.ts +12 -0
  17. package/dist/cua-admission-limit.js +20 -0
  18. package/dist/cua-admission-limit.js.map +1 -0
  19. package/dist/cua-diagnostics.d.ts +38 -0
  20. package/dist/cua-diagnostics.js +68 -0
  21. package/dist/cua-diagnostics.js.map +1 -0
  22. package/dist/e2b-desktop-executor.js +4 -2
  23. package/dist/e2b-desktop-executor.js.map +1 -1
  24. package/dist/feedback.js +5 -3
  25. package/dist/feedback.js.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +2 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/observer-data.js +11 -4
  30. package/dist/observer-data.js.map +1 -1
  31. package/dist/openai-responses-cu.js +13 -2
  32. package/dist/openai-responses-cu.js.map +1 -1
  33. package/dist/program.d.ts +2 -0
  34. package/dist/program.js +9 -4
  35. package/dist/program.js.map +1 -1
  36. package/dist/run.d.ts +18 -5
  37. package/dist/run.js +70 -2
  38. package/dist/run.js.map +1 -1
  39. package/dist/stats.js +1 -1
  40. package/dist/stats.js.map +1 -1
  41. package/dist/telemetry.d.ts +3 -0
  42. package/dist/telemetry.js +15 -1
  43. package/dist/telemetry.js.map +1 -1
  44. package/docs/architecture/examples/state-driven-local-app/README.md +75 -0
  45. package/docs/architecture/examples/state-driven-local-app/app.mjs +48 -0
  46. package/docs/architecture/examples/state-driven-local-app/runner.mjs +104 -0
  47. package/docs/architecture/state-driven-executor.md +19 -26
  48. package/docs/contracts/adapter-admission.md +54 -0
  49. package/docs/contracts/run-bundle.md +8 -1
  50. package/docs/contracts/schemas.md +16 -2
  51. package/docs/goals/current.md +9 -4
  52. package/docs/ramp/README.md +11 -1
  53. package/docs/release/0.88.0-study-diagnostics.md +43 -0
  54. package/docs/release/0.88.1-completion-evidence-and-local-app.md +79 -0
  55. package/package.json +1 -1
package/dist/run.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type CodexAppServerTrace } from "./codex-app-server.js";
2
2
  import { type ActorStatus, type ActorTrace, type ActorTraceItem } from "./actor-contract.js";
3
+ import { type CuaGoalSource } from "./actor-goal-source.js";
3
4
  import type { TaskFunnel } from "./tasks.js";
4
5
  import { type CapturedGitState } from "./core/git-state.js";
5
6
  import type { E2BDesktopModule } from "./e2b-desktop-launch.js";
@@ -844,7 +845,7 @@ export interface RunCostLine {
844
845
  modelId?: string;
845
846
  /** null = NOT MEASURED / no rate; never coerced to 0. */
846
847
  estimatedCostUsd: number | null;
847
- reason?: "no_rate_for_model" | "no_rate_for_desktop" | "no_token_usage" | "no_duration" | "closing_usage_unreported" | "no_desktop_resources" | "desktop_lifetime_incomplete";
848
+ reason?: "no_rate_for_model" | "no_rate_for_desktop" | "no_token_usage" | "no_duration" | "closing_usage_unreported" | "interaction_usage_unreported" | "no_desktop_resources" | "desktop_lifetime_incomplete";
848
849
  /** Pricing provenance date; non-null iff estimatedCostUsd is non-null. */
849
850
  ratesAsOf: string | null;
850
851
  source?: string;
@@ -933,7 +934,7 @@ export interface RunRerunLineage {
933
934
  export interface ParticipantOutcomes {
934
935
  /** Participants whose sessions reached a terminal state — the denominator for every count below. */
935
936
  total: number;
936
- /** Reached the goal. */
937
+ /** Recorded successful sessions; completion provenance depends on the actor and its evidence. */
937
938
  reachedGoal: number;
938
939
  /** Stopped trying. A finding about the product. */
939
940
  abandoned: number;
@@ -1011,11 +1012,23 @@ export interface StudyTaskFunnel {
1011
1012
  export declare function aggregateTaskFunnels(funnels: readonly TaskFunnel[]): StudyTaskFunnel | undefined;
1012
1013
  /** The funnel as one line, denominator on every number: `signup 2/2 · verify-email 1/2`. */
1013
1014
  export declare function formatStudyTaskFunnel(funnel: StudyTaskFunnel): string;
1014
- /** One line a stakeholder can read, with the denominator attached to every number. */
1015
- export declare function formatParticipantOutcomes(outcomes: ParticipantOutcomes, terminalCauses?: readonly {
1015
+ type ParticipantOutcomeDetail = {
1016
1016
  status: ActorStatus;
1017
1017
  label?: string;
1018
- }[]): string;
1018
+ goalSource?: CuaGoalSource;
1019
+ };
1020
+ /** One line a stakeholder can read, with the denominator attached to every number. */
1021
+ export declare function formatParticipantOutcomes(outcomes: ParticipantOutcomes, terminalCauses?: readonly ParticipantOutcomeDetail[]): string;
1022
+ /** Presentation details tolerate optional legacy actor payloads without changing their tallies. */
1023
+ export declare function participantOutcomeDetails(streams: readonly {
1024
+ actor?: unknown;
1025
+ status?: unknown;
1026
+ }[]): ParticipantOutcomeDetail[];
1027
+ /** Refresh a CUA completion claim from recorded traces, leaving original evidence and enums intact. */
1028
+ export declare function withCuaReviewProvenance(review: ReviewSummary, streams: readonly {
1029
+ actor?: unknown;
1030
+ status?: unknown;
1031
+ }[]): ReviewSummary;
1019
1032
  export declare function buildRunSource(args: {
1020
1033
  cwd: string;
1021
1034
  capturedAt?: Date | string;
package/dist/run.js CHANGED
@@ -13,6 +13,8 @@ import { CODEX_APP_SERVER_TRACE_SCHEMA, runCodexAppServerSessionInPreparedRoot }
13
13
  import { getActor } from "./actor-registry.js";
14
14
  import { artifactReferenceIfWritten, hasWrittenScreenshot } from "./artifact-reference.js";
15
15
  import { ACTOR_TRACE_SCHEMA } from "./actor-contract.js";
16
+ import { cuaGoalSource, isCuaTrace, CUA_COMPLETION_NOTE } from "./actor-goal-source.js";
17
+ import { actorEnding } from "./actor-stop-cause.js";
16
18
  import { captureGitState, GIT_STATE_SCHEMA } from "./core/git-state.js";
17
19
  import { inspectVerifiedGitWorkspace } from "./core/git-workspace.js";
18
20
  import { mapWithConcurrency } from "./concurrency.js";
@@ -137,11 +139,34 @@ export function formatStudyTaskFunnel(funnel) {
137
139
  })
138
140
  .join(" · ");
139
141
  }
142
+ function participantCompletionLine(outcomes, terminalCauses) {
143
+ const completions = terminalCauses.filter((entry) => entry.status === "passed");
144
+ const reported = completions.filter((entry) => entry.goalSource === "participant_report").length;
145
+ const matched = completions.filter((entry) => entry.goalSource === "condition_matched").length;
146
+ let goalLine = outcomes.reachedGoal === 0
147
+ ? `0/${outcomes.total} recorded completions`
148
+ : `${outcomes.reachedGoal}/${outcomes.total} reached the goal`;
149
+ if (outcomes.reachedGoal > 0 && terminalCauses.some((entry) => entry.goalSource !== undefined)) {
150
+ const count = `${outcomes.reachedGoal}/${outcomes.total}`;
151
+ goalLine = completions.length !== outcomes.reachedGoal
152
+ ? `${count} recorded completions (completion source unavailable)`
153
+ : reported === outcomes.reachedGoal
154
+ ? `${count} reported reaching the goal`
155
+ : matched === outcomes.reachedGoal
156
+ ? `${count} met a recorded completion condition`
157
+ : `${count} recorded completions (${[
158
+ reported > 0 ? `${reported} participant-reported` : undefined,
159
+ matched > 0 ? `${matched} condition-matched` : undefined,
160
+ outcomes.reachedGoal - reported - matched > 0 ? `${outcomes.reachedGoal - reported - matched} other or unavailable source` : undefined
161
+ ].filter(Boolean).join(", ")})`;
162
+ }
163
+ return goalLine;
164
+ }
140
165
  /** One line a stakeholder can read, with the denominator attached to every number. */
141
166
  export function formatParticipantOutcomes(outcomes, terminalCauses = []) {
142
167
  if (outcomes.total === 0)
143
168
  return "no participants reached a terminal state";
144
- const parts = [`${outcomes.reachedGoal}/${outcomes.total} reached the goal`];
169
+ const parts = [participantCompletionLine(outcomes, terminalCauses)];
145
170
  // Detail may explain a recorded outcome, but must never change its count or invent a match
146
171
  // between a tally and an incomplete set of traces.
147
172
  const append = (count, statuses, fallback) => {
@@ -174,6 +199,46 @@ export function formatParticipantOutcomes(outcomes, terminalCauses = []) {
174
199
  parts.push(`${outcomes.reportedFriction} reported friction`);
175
200
  return parts.join(", ");
176
201
  }
202
+ /** Presentation details tolerate optional legacy actor payloads without changing their tallies. */
203
+ export function participantOutcomeDetails(streams) {
204
+ return streams.flatMap((stream) => {
205
+ if (!isRecord(stream.actor))
206
+ return [];
207
+ const actor = stream.actor;
208
+ const goalSource = cuaGoalSource(actor, stream.status);
209
+ if (!["passed", "abandoned", "incomplete", "blocked", "timed_out", "failed"].includes(String(actor.status))) {
210
+ return goalSource === "unavailable" ? [{ status: "passed", goalSource }] : [];
211
+ }
212
+ const ending = Array.isArray(actor.items) && actor.items.every(isRecord) ? actorEnding(actor) : undefined;
213
+ return [{ status: actor.status,
214
+ ...(ending === undefined ? {} : { label: ending.label }),
215
+ ...(goalSource === undefined ? {} : { goalSource }) }];
216
+ });
217
+ }
218
+ /** Refresh a CUA completion claim from recorded traces, leaving original evidence and enums intact. */
219
+ export function withCuaReviewProvenance(review, streams) {
220
+ const details = participantOutcomeDetails(streams);
221
+ if (!isRecord(review.participants)
222
+ || !["total", "reachedGoal", "abandoned", "ranOut", "blocked", "harnessFailed", "reportedFriction"].every((key) => isNonNegativeSafeInteger(review.participants[key]))
223
+ || (review.participants.reachedGoal === 0
224
+ ? !streams.some((stream) => isCuaTrace(stream.actor))
225
+ : !details.some((entry) => entry.goalSource !== undefined)))
226
+ return review;
227
+ const outcomes = formatParticipantOutcomes(review.participants, details);
228
+ // Preserve rerun context, participant narration and adapter-specific findings. Refreshing a
229
+ // historical summary qualifies its old tally instead of silently discarding that context.
230
+ const header = `Run gate: ${review.verdict}. Participants: ${outcomes}.${review.tasks ? ` Tasks: ${formatStudyTaskFunnel(review.tasks)}.` : ""}`;
231
+ const prefix = `${header} Recorded summary: `;
232
+ const recorded = review.summary.startsWith(prefix) ? review.summary.slice(prefix.length) : review.summary;
233
+ const oldGoal = `${review.participants.reachedGoal}/${review.participants.total} reached the goal`;
234
+ const qualified = recorded.split(oldGoal).join(participantCompletionLine(review.participants, details));
235
+ return {
236
+ ...review,
237
+ summary: `${prefix}${qualified}`,
238
+ gaps: review.participants.reachedGoal === 0 ? review.gaps
239
+ : [...review.gaps.filter((gap) => gap !== CUA_COMPLETION_NOTE), CUA_COMPLETION_NOTE]
240
+ };
241
+ }
177
242
  export async function buildRunSource(args) {
178
243
  const gitOptions = args.capturedAt === undefined ? {} : { capturedAt: args.capturedAt };
179
244
  return {
@@ -2988,8 +3053,11 @@ export async function readReview(cwdInput, runInput) {
2988
3053
  }
2989
3054
  };
2990
3055
  }
3056
+ const bundle = runPaths ? await readRunJsonIfExists(runPaths, "run.json") : null;
3057
+ const projected = isRecord(bundle) && Array.isArray(bundle.streams)
3058
+ ? withCuaReviewProvenance(review, bundle.streams.filter(isRecord)) : review;
2991
3059
  return {
2992
- ...review,
3060
+ ...projected,
2993
3061
  path: path.relative(cwd, path.join(runPaths.absoluteRunRoot, "review.json")),
2994
3062
  runId: path.basename(runPaths.absoluteRunRoot)
2995
3063
  };