humanish 0.117.0 → 0.119.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 (99) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/analysis/admission.d.ts +23 -4
  3. package/dist/analysis/admission.js +47 -9
  4. package/dist/analysis/analysis-limits.d.ts +32 -0
  5. package/dist/analysis/analysis-limits.js +41 -3
  6. package/dist/analysis/automatic-config.d.ts +11 -1
  7. package/dist/analysis/automatic-config.js +26 -11
  8. package/dist/analysis/cohorts.d.ts +103 -0
  9. package/dist/analysis/cohorts.js +80 -0
  10. package/dist/analysis/evidence.d.ts +5 -10
  11. package/dist/analysis/evidence.js +37 -34
  12. package/dist/analysis/execute.d.ts +24 -7
  13. package/dist/analysis/execute.js +290 -307
  14. package/dist/analysis/responses.d.ts +19 -0
  15. package/dist/analysis/responses.js +201 -0
  16. package/dist/analysis/store.js +2 -1
  17. package/dist/analysis/validation.js +13 -7
  18. package/dist/cli/commands/analyze.js +10 -4
  19. package/dist/cli/commands/study-format.js +8 -11
  20. package/dist/cli/commands/study-route-shared-world.js +3 -1
  21. package/dist/cli/commands/study-run.js +1 -1
  22. package/dist/cli/doctor.js +11 -1
  23. package/dist/comms/lease-store.d.ts +1 -1
  24. package/dist/comms/lease-store.js +3 -2
  25. package/dist/comms/receiving-types.d.ts +2 -0
  26. package/dist/comms/receiving-types.js +2 -0
  27. package/dist/observer-app.html +2 -2
  28. package/dist/routes/computer-use/fanout-bundle.js +4 -2
  29. package/dist/routes/computer-use/fanout-records.js +6 -1
  30. package/dist/routes/computer-use/participant-execution.d.ts +6 -4
  31. package/dist/routes/computer-use/participant-execution.js +42 -14
  32. package/dist/routes/computer-use/participant-model.js +18 -8
  33. package/dist/routes/computer-use/participant-runs.js +15 -9
  34. package/dist/routes/computer-use/plan.d.ts +3 -1
  35. package/dist/routes/computer-use/plan.js +38 -12
  36. package/dist/routes/computer-use/rerun-selection.js +14 -3
  37. package/dist/routes/computer-use/single-bundle.js +7 -4
  38. package/dist/routes/computer-use/subject-projection.js +2 -0
  39. package/dist/routes/computer-use/types.d.ts +13 -1
  40. package/dist/routes/scripted/plan.d.ts +4 -1
  41. package/dist/routes/scripted/plan.js +25 -2
  42. package/dist/routes/scripted/setup.js +1 -4
  43. package/dist/routes/scripted/types.d.ts +1 -1
  44. package/dist/routes/shared-world/bundle-records.js +14 -0
  45. package/dist/routes/shared-world/bundle.js +2 -1
  46. package/dist/routes/shared-world/external-public.js +15 -3
  47. package/dist/routes/shared-world/handoff.d.ts +2 -2
  48. package/dist/routes/shared-world/participant-specs.js +2 -2
  49. package/dist/routes/shared-world/plan.d.ts +10 -1
  50. package/dist/routes/shared-world/plan.js +86 -10
  51. package/dist/routes/shared-world/provisioned.js +15 -8
  52. package/dist/routes/shared-world/route.js +7 -4
  53. package/dist/routes/shared-world/types.d.ts +2 -0
  54. package/dist/run/bundle-shape.js +12 -2
  55. package/dist/run/bundle.d.ts +20 -3
  56. package/dist/run/participant-records.js +1 -0
  57. package/dist/run/projection.js +7 -2
  58. package/dist/run/run.js +4 -2
  59. package/dist/run/subject-name.d.ts +11 -0
  60. package/dist/run/subject-name.js +11 -0
  61. package/dist/study/arrivals.d.ts +114 -0
  62. package/dist/study/arrivals.js +204 -0
  63. package/dist/study/composition-rules.js +1 -1
  64. package/dist/study/config.js +6 -20
  65. package/dist/study/doctor.js +2 -1
  66. package/dist/study/keys.d.ts +1 -0
  67. package/dist/study/keys.js +2 -0
  68. package/dist/study/migrate/v2.js +3 -2
  69. package/dist/study/parse/actors.d.ts +2 -2
  70. package/dist/study/parse/actors.js +10 -4
  71. package/dist/study/parse/front.d.ts +1 -1
  72. package/dist/study/parse/front.js +36 -2
  73. package/dist/study/plan-base.d.ts +1 -1
  74. package/dist/study/plan-participants.d.ts +2 -0
  75. package/dist/study/plan-participants.js +3 -5
  76. package/dist/study/plan-types.d.ts +13 -3
  77. package/dist/study/plan.d.ts +4 -4
  78. package/dist/study/plan.js +21 -12
  79. package/dist/study/preflight.d.ts +2 -1
  80. package/dist/study/preflight.js +24 -3
  81. package/dist/study/routing.d.ts +5 -4
  82. package/dist/study/routing.js +16 -7
  83. package/dist/study/types.d.ts +14 -0
  84. package/dist/study/validation.d.ts +2 -2
  85. package/dist/study/validation.js +27 -14
  86. package/dist/study/warnings.js +11 -3
  87. package/dist/substrates/e2b/lifetime.d.ts +75 -4
  88. package/dist/substrates/e2b/lifetime.js +134 -10
  89. package/dist/substrates/e2b/subject-sandbox.js +2 -6
  90. package/dist/tui-app.js +112 -112
  91. package/dist/verify/artifacts.d.ts +19 -4
  92. package/dist/verify/artifacts.js +39 -8
  93. package/dist/verify/verify.js +6 -15
  94. package/docs/contracts/run-bundle.md +8 -7
  95. package/docs/contracts/schemas.md +33 -9
  96. package/docs/contracts/study-analysis.md +61 -8
  97. package/docs/principles/invariants-and-defaults.md +14 -14
  98. package/docs/product/automatic-analysis.md +10 -3
  99. package/package.json +1 -1
@@ -8,15 +8,8 @@ import { ACTION_CAPTURE_VERSION, } from "./types.js";
8
8
  import { digestAnalysisInput, hashAnalysisValue, validateAnalysisInputMetadata, } from "./validation.js";
9
9
  import { boundedText, fairShares, hasUnmappedCaptures, participantAssignment, participantSource, sourceEntries, sourceOrder, impressionEventIds, } from "./evidence-sources.js";
10
10
  import { readBoundedFile, readBoundedFileResult } from "../run/evidence-files.js";
11
- export const EVIDENCE_LIMITS = Object.freeze({
12
- participants: 16,
13
- evidence: 800,
14
- captures: 40,
15
- textBytes: 160 * 1024,
16
- imageBytes: 8 * 1024 * 1024,
17
- totalImageBytes: 20 * 1024 * 1024,
18
- sourceBytes: 16 * 1024 * 1024,
19
- });
11
+ import { analysisCohortCount, analysisCohorts, EVIDENCE_LIMITS } from "./analysis-limits.js";
12
+ export { EVIDENCE_LIMITS };
20
13
  // A second guard for direct callers. The analysis service already requires every stream to be in
21
14
  // TERMINAL_SIMULATION_STATUSES; this refuses only statuses that mean still running, so a dry-run
22
15
  // bundle's contract_proof_only streams stay capturable. The source is not shape-guarded here, so
@@ -39,7 +32,7 @@ function parseSource(prepared, bytes) {
39
32
  value.schema !== "humanish.run-bundle.v1" ||
40
33
  value.runId !== runIdOf(prepared) ||
41
34
  !Array.isArray(value.streams) ||
42
- value.streams.length > 128 ||
35
+ value.streams.length > EVIDENCE_LIMITS.participants ||
43
36
  !Array.isArray(value.events) ||
44
37
  value.events.length > 100000 ||
45
38
  (value.commsReceiving !== undefined && !isCommsReceivingEvidence(value.commsReceiving)))
@@ -108,7 +101,7 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
108
101
  const evidence = [];
109
102
  const images = [];
110
103
  const omissions = new Set();
111
- let textBytes = 0;
104
+ const contextBytes = new Map();
112
105
  const participants = selected.map((stream) => {
113
106
  const participant = participantSource(stream, captureVersion);
114
107
  const assignment = participantAssignment(stream, captureVersion);
@@ -118,30 +111,38 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
118
111
  participant.assignment !== assignment ||
119
112
  participant.recordedReason !== (stream.actor?.reason ?? null))
120
113
  omissions.add("Participant context exceeded the text limit.");
121
- textBytes += Buffer.byteLength(JSON.stringify(participant));
114
+ contextBytes.set(stream, Buffer.byteLength(JSON.stringify(participant)));
122
115
  return participant;
123
116
  });
124
- if (textBytes > limits.textBytes)
125
- throw new Error("ANALYSIS_PARTICIPANT_CONTEXT_TOO_LARGE");
126
- // Tie-breaking uses stable participant IDs, not the order of streams in a bundle.
127
- // The emitted packet still preserves participant and source event order.
128
- const packings = selected
129
- .map((stream) => ({
130
- stream,
131
- entries: sourceEntries(bundle, stream, captureVersion),
132
- captures: new Map(),
133
- admitted: new Set(),
134
- captureCursor: 0,
135
- attempts: 0,
136
- readBytes: 0,
137
- imageBytes: 0,
138
- reservationBlocked: false,
139
- }))
140
- .sort((a, b) => (a.stream.id < b.stream.id ? -1 : a.stream.id > b.stream.id ? 1 : 0));
141
- const sel = planCaptureSelection(packings, limits);
142
- await selectCaptures(prepared, packings, sel, omissions);
143
- recordCaptureOmissions(packings, sel, omissions);
144
- const textByEntry = boundEvidenceText(packings, limits.textBytes - textBytes, omissions);
117
+ // Each cohort is one request's packet, selected under the full limits on its own.
118
+ const packings = [];
119
+ const textByEntry = new Map();
120
+ for (const cohort of analysisCohorts(selected)) {
121
+ const textBytes = cohort.reduce((total, stream) => total + contextBytes.get(stream), 0);
122
+ if (textBytes > limits.textBytes)
123
+ throw new Error("ANALYSIS_PARTICIPANT_CONTEXT_TOO_LARGE");
124
+ // Tie-breaking uses stable participant IDs, not the order of streams in a bundle.
125
+ // The emitted packet still preserves participant and source event order.
126
+ const cohortPackings = cohort
127
+ .map((stream) => ({
128
+ stream,
129
+ entries: sourceEntries(bundle, stream, captureVersion),
130
+ captures: new Map(),
131
+ admitted: new Set(),
132
+ captureCursor: 0,
133
+ attempts: 0,
134
+ readBytes: 0,
135
+ imageBytes: 0,
136
+ reservationBlocked: false,
137
+ }))
138
+ .sort((a, b) => (a.stream.id < b.stream.id ? -1 : a.stream.id > b.stream.id ? 1 : 0));
139
+ const sel = planCaptureSelection(cohortPackings, limits);
140
+ await selectCaptures(prepared, cohortPackings, sel, omissions);
141
+ recordCaptureOmissions(cohortPackings, sel, omissions);
142
+ for (const [entry, text] of boundEvidenceText(cohortPackings, limits.textBytes - textBytes, omissions))
143
+ textByEntry.set(entry, text);
144
+ packings.push(...cohortPackings);
145
+ }
145
146
  packEvidence(selected, packings, textByEntry, evidence, images);
146
147
  const omittedStreamIds = bundle.streams.slice(limits.participants).map((stream) => stream.id);
147
148
  const coverage = {
@@ -425,6 +426,8 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
425
426
  }
426
427
  const sourceKeys = new Set();
427
428
  const checkedCaptures = new Map();
429
+ // Each cohort's packet holds at most totalImageBytes.
430
+ const imageByteLimit = analysisCohortCount(included.size) * EVIDENCE_LIMITS.totalImageBytes;
428
431
  let checkedImageBytes = 0;
429
432
  for (const entry of artifact.evidence) {
430
433
  const matches = sourceByStream
@@ -462,7 +465,7 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
462
465
  if (!bytes || screenshotEvidenceError(source.capturePath, bytes) !== null)
463
466
  throw new Error("ANALYSIS_CAPTURE_UNAVAILABLE");
464
467
  checkedImageBytes += bytes.length;
465
- if (checkedImageBytes > EVIDENCE_LIMITS.totalImageBytes)
468
+ if (checkedImageBytes > imageByteLimit)
466
469
  throw new Error("ANALYSIS_IMAGE_LIMIT_EXCEEDED");
467
470
  hash = sha256(bytes);
468
471
  checkedCaptures.set(source.capturePath, hash);
@@ -23,6 +23,9 @@ export interface AnalysisAdmission {
23
23
  admittedCostUsd: number | null;
24
24
  maxCostUsd: number | null;
25
25
  ratesAsOf: string | null;
26
+ /** The provider requests the costs cover: one per cohort, and the merge request for more than
27
+ * one cohort. Null when the input or configuration was refused. */
28
+ requests: number | null;
26
29
  }
27
30
  export interface AnalysisProgress {
28
31
  phase: "admitted" | "requesting" | "validating" | "finished";
@@ -43,14 +46,28 @@ export interface AnalysisProgress {
43
46
  */
44
47
  export declare function estimateAnalysisAdmission(input: AnalysisInput, config: AnalysisConfig): AnalysisAdmission;
45
48
  /**
46
- * The expected cost of an analysis for this many participants before any evidence exists: from a
47
- * packet with no evidence to one at the evidence limits with every capture at the image-token
48
- * ceiling. Undefined for Codex and for a model without rates.
49
+ * The expected cost of the analysis admission would start, before any evidence exists: from a
50
+ * packet with no evidence to the largest packet admission admits. When admission refuses even a
51
+ * packet with no evidence, `refusedFromUsd` is that packet's expected cost.
49
52
  */
50
- export declare function analysisCostRange(config: AnalysisConfig, participants: number): {
51
- low: number;
52
- high: number;
53
- } | undefined;
53
+ export type AnalysisCostRange = {
54
+ expectedCostUsd: {
55
+ low: number;
56
+ high: number;
57
+ };
58
+ } | {
59
+ refusedFromUsd: number;
60
+ };
61
+ /**
62
+ * The expected cost of the analysis admission would start for this many participants, before any
63
+ * evidence exists. Packets grow from no evidence toward the evidence limits with every capture at
64
+ * the image-token ceiling, and the range ends at the largest one admission admits, so it never
65
+ * passes the cap. There is one packet per cohort, each a share of its own limits, and the merge
66
+ * request when there is more than one cohort. With `preferLargerOutput` each request gets the
67
+ * output allowance dispatch would give it (preferLargerAnalysisOutput). Undefined for Codex and
68
+ * for a model without rates.
69
+ */
70
+ export declare function analysisCostRange(config: AnalysisConfig, participants: number, preferLargerOutput?: boolean): AnalysisCostRange | undefined;
54
71
  /** Only for an omitted output limit. Preserve the established allowance when
55
72
  * more reasoning/report space would refuse a study its declared budget admits.
56
73
  * This is one pre-dispatch choice, never a fallback request or a budget increase. */