@openpond/evals 0.11.16 → 0.11.17

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 (158) hide show
  1. package/dist/connected-evidence/contracts.js +72 -0
  2. package/dist/connected-evidence/imports.js +71 -0
  3. package/dist/connected-evidence/index.js +5 -0
  4. package/dist/connected-evidence/normalize.js +97 -0
  5. package/dist/connected-evidence/python-correctness.js +73 -0
  6. package/dist/connected-evidence/sources/claude-code.js +71 -0
  7. package/dist/connected-evidence/sources/codex.js +52 -0
  8. package/dist/connected-evidence/sources/history.js +86 -0
  9. package/dist/connected-evidence/sources/usage.js +15 -0
  10. package/dist/connected-evidence/usage-summary.js +52 -0
  11. package/dist/experiments.js +16 -4
  12. package/dist/graders.js +9 -6
  13. package/dist/human-review/comparison.js +161 -0
  14. package/dist/human-review/contracts.js +110 -0
  15. package/dist/human-review/execution.js +61 -0
  16. package/dist/human-review/handoff.js +18 -0
  17. package/dist/human-review/index.js +9 -0
  18. package/dist/human-review/local-publication.js +18 -0
  19. package/dist/human-review/repository.js +1 -0
  20. package/dist/human-review/results.js +18 -0
  21. package/dist/human-review/service.js +268 -0
  22. package/dist/human-review/task-proposals.js +39 -0
  23. package/dist/index.js +1 -0
  24. package/dist/javascript-verifier-contract.js +2 -2
  25. package/dist/learning/authoring.js +2 -1
  26. package/dist/learning/contracts.js +353 -81
  27. package/dist/learning/index.js +1 -0
  28. package/dist/learning/reward-authoring.js +5 -2
  29. package/dist/learning/training-handoff.js +32 -0
  30. package/dist/metrics-node.js +2 -0
  31. package/dist/profile-evaluation-target.js +7 -0
  32. package/dist/profile-evaluations.js +7 -10
  33. package/dist/profile-external-evaluation.js +35 -0
  34. package/dist/profile-source-candidate.js +5 -0
  35. package/dist/rewards.js +5 -3
  36. package/dist/taskset-run-contract.js +10 -0
  37. package/dist/tasksets.js +2 -0
  38. package/dist/types/builtin-benchmarks/harness-refiner.d.ts +75 -1
  39. package/dist/types/builtin-benchmarks/harness-refiner.d.ts.map +1 -1
  40. package/dist/types/conformance.d.ts +150 -2
  41. package/dist/types/conformance.d.ts.map +1 -1
  42. package/dist/types/connected-evidence/contracts.d.ts +317 -0
  43. package/dist/types/connected-evidence/contracts.d.ts.map +1 -0
  44. package/dist/types/connected-evidence/imports.d.ts +19 -0
  45. package/dist/types/connected-evidence/imports.d.ts.map +1 -0
  46. package/dist/types/connected-evidence/index.d.ts +6 -0
  47. package/dist/types/connected-evidence/index.d.ts.map +1 -0
  48. package/dist/types/connected-evidence/normalize.d.ts +25 -0
  49. package/dist/types/connected-evidence/normalize.d.ts.map +1 -0
  50. package/dist/types/connected-evidence/python-correctness.d.ts +196 -0
  51. package/dist/types/connected-evidence/python-correctness.d.ts.map +1 -0
  52. package/dist/types/connected-evidence/sources/claude-code.d.ts +74 -0
  53. package/dist/types/connected-evidence/sources/claude-code.d.ts.map +1 -0
  54. package/dist/types/connected-evidence/sources/codex.d.ts +74 -0
  55. package/dist/types/connected-evidence/sources/codex.d.ts.map +1 -0
  56. package/dist/types/connected-evidence/sources/history.d.ts +146 -0
  57. package/dist/types/connected-evidence/sources/history.d.ts.map +1 -0
  58. package/dist/types/connected-evidence/sources/usage.d.ts +3 -0
  59. package/dist/types/connected-evidence/sources/usage.d.ts.map +1 -0
  60. package/dist/types/connected-evidence/usage-summary.d.ts +162 -0
  61. package/dist/types/connected-evidence/usage-summary.d.ts.map +1 -0
  62. package/dist/types/evidence/conformance.d.ts +28 -28
  63. package/dist/types/evidence/contracts.d.ts +27 -27
  64. package/dist/types/execution-contracts.d.ts +159 -5
  65. package/dist/types/execution-contracts.d.ts.map +1 -1
  66. package/dist/types/experiments.d.ts +476 -7
  67. package/dist/types/experiments.d.ts.map +1 -1
  68. package/dist/types/graders.d.ts +21 -1
  69. package/dist/types/graders.d.ts.map +1 -1
  70. package/dist/types/human-review/comparison.d.ts +1959 -0
  71. package/dist/types/human-review/comparison.d.ts.map +1 -0
  72. package/dist/types/human-review/contracts.d.ts +2719 -0
  73. package/dist/types/human-review/contracts.d.ts.map +1 -0
  74. package/dist/types/human-review/execution.d.ts +298 -0
  75. package/dist/types/human-review/execution.d.ts.map +1 -0
  76. package/dist/types/human-review/handoff.d.ts +124 -0
  77. package/dist/types/human-review/handoff.d.ts.map +1 -0
  78. package/dist/types/human-review/index.d.ts +10 -0
  79. package/dist/types/human-review/index.d.ts.map +1 -0
  80. package/dist/types/human-review/local-publication.d.ts +165 -0
  81. package/dist/types/human-review/local-publication.d.ts.map +1 -0
  82. package/dist/types/human-review/repository.d.ts +43 -0
  83. package/dist/types/human-review/repository.d.ts.map +1 -0
  84. package/dist/types/human-review/results.d.ts +118 -0
  85. package/dist/types/human-review/results.d.ts.map +1 -0
  86. package/dist/types/human-review/service.d.ts +878 -0
  87. package/dist/types/human-review/service.d.ts.map +1 -0
  88. package/dist/types/human-review/task-proposals.d.ts +70 -0
  89. package/dist/types/human-review/task-proposals.d.ts.map +1 -0
  90. package/dist/types/index.d.ts +1 -0
  91. package/dist/types/index.d.ts.map +1 -1
  92. package/dist/types/javascript-environment-contract.d.ts +1 -1
  93. package/dist/types/javascript-verifier-contract.d.ts +1 -1
  94. package/dist/types/javascript-verifier-contract.d.ts.map +1 -1
  95. package/dist/types/learned-preference.d.ts +2 -2
  96. package/dist/types/learning/acceptance-group.d.ts +13 -13
  97. package/dist/types/learning/acceptance-plan.d.ts +1 -1
  98. package/dist/types/learning/admission.d.ts +78 -1
  99. package/dist/types/learning/admission.d.ts.map +1 -1
  100. package/dist/types/learning/authoring-service.d.ts +2 -0
  101. package/dist/types/learning/authoring-service.d.ts.map +1 -1
  102. package/dist/types/learning/authoring.d.ts +20 -0
  103. package/dist/types/learning/authoring.d.ts.map +1 -1
  104. package/dist/types/learning/batch-service.d.ts +76 -2
  105. package/dist/types/learning/batch-service.d.ts.map +1 -1
  106. package/dist/types/learning/contracts.d.ts +759 -24
  107. package/dist/types/learning/contracts.d.ts.map +1 -1
  108. package/dist/types/learning/index.d.ts +1 -0
  109. package/dist/types/learning/index.d.ts.map +1 -1
  110. package/dist/types/learning/iteration-candidate-decision.d.ts +2 -2
  111. package/dist/types/learning/iteration-dispatch-contracts.d.ts +2 -2
  112. package/dist/types/learning/operations.d.ts +1118 -8
  113. package/dist/types/learning/operations.d.ts.map +1 -1
  114. package/dist/types/learning/repository.d.ts +402 -15
  115. package/dist/types/learning/repository.d.ts.map +1 -1
  116. package/dist/types/learning/reward-authoring.d.ts.map +1 -1
  117. package/dist/types/learning/reward-calibration.d.ts +74 -0
  118. package/dist/types/learning/reward-calibration.d.ts.map +1 -1
  119. package/dist/types/learning/reward-checks.d.ts +14 -2
  120. package/dist/types/learning/reward-checks.d.ts.map +1 -1
  121. package/dist/types/learning/training-handoff.d.ts +201 -0
  122. package/dist/types/learning/training-handoff.d.ts.map +1 -0
  123. package/dist/types/learning/transport.d.ts +963 -21
  124. package/dist/types/learning/transport.d.ts.map +1 -1
  125. package/dist/types/metrics-node.d.ts +3 -1
  126. package/dist/types/metrics-node.d.ts.map +1 -1
  127. package/dist/types/model-improvement-qualification.d.ts +3 -3
  128. package/dist/types/native-harness-experiment-attempt.d.ts +2 -2
  129. package/dist/types/profile-evaluation-comparisons.d.ts +230 -0
  130. package/dist/types/profile-evaluation-comparisons.d.ts.map +1 -1
  131. package/dist/types/profile-evaluation-target.d.ts +14 -0
  132. package/dist/types/profile-evaluation-target.d.ts.map +1 -0
  133. package/dist/types/profile-evaluations.d.ts +117 -14
  134. package/dist/types/profile-evaluations.d.ts.map +1 -1
  135. package/dist/types/profile-external-evaluation.d.ts +404 -0
  136. package/dist/types/profile-external-evaluation.d.ts.map +1 -0
  137. package/dist/types/profile-source-candidate.d.ts +15 -0
  138. package/dist/types/profile-source-candidate.d.ts.map +1 -0
  139. package/dist/types/review-conformance.d.ts +7 -7
  140. package/dist/types/rewards.d.ts +253 -0
  141. package/dist/types/rewards.d.ts.map +1 -1
  142. package/dist/types/rollouts.d.ts +3 -3
  143. package/dist/types/taskset-run-contract.d.ts +230 -0
  144. package/dist/types/taskset-run-contract.d.ts.map +1 -1
  145. package/dist/types/tasksets.d.ts +311 -3
  146. package/dist/types/tasksets.d.ts.map +1 -1
  147. package/dist/types/telemetry-analysis.d.ts +2 -2
  148. package/dist/types/telemetry-bundle.d.ts +6 -6
  149. package/dist/types/telemetry.d.ts +3 -3
  150. package/package.json +9 -1
  151. package/schemas/learning/v1/command-request.schema.json +1745 -409
  152. package/schemas/learning/v1/decision.schema.json +11 -0
  153. package/schemas/learning/v1/draft.schema.json +11 -0
  154. package/schemas/learning/v1/grade.schema.json +11 -0
  155. package/schemas/learning/v1/package.schema.json +532 -12
  156. package/schemas/learning/v1/policy.schema.json +836 -28
  157. package/schemas/learning/v1/reward.schema.json +520 -0
  158. package/schemas/learning/v1/reward_check.schema.json +9 -0
@@ -0,0 +1,268 @@
1
+ import { assertHumanTaskProposal, redactHumanTaskProposal } from "./task-proposals.js";
2
+ import { contentHash } from "@openpond/harness";
3
+ import { LearningDomainError } from "../learning/errors.js";
4
+ import { assertHumanAnswers, HumanReviewCommandSchema, HumanReviewSchema, sealHumanRecord } from "./contracts.js";
5
+ const closed = (r) => ["accepted", "rejected", "cancelled"].includes(r.status);
6
+ function fail(code, status = 422) { throw new LearningDomainError(code, status); }
7
+ function answers(record, value, complete) {
8
+ try {
9
+ assertHumanAnswers(record.form, value, complete);
10
+ }
11
+ catch (e) {
12
+ fail(e instanceof Error ? e.message : "human_answer_invalid");
13
+ }
14
+ }
15
+ export function createHumanReviewService(repository, authority, now = () => new Date().toISOString()) {
16
+ async function access(context, record) {
17
+ if (record.scope !== context.scope)
18
+ fail("human_scope_denied", 403);
19
+ await authority.member(context);
20
+ const owner = await authority.owner(context, record.projectId);
21
+ const manager = owner || record.policy.managerIds.includes(context.actorId);
22
+ const reviewer = owner || record.assigneeId === context.actorId || record.policy.reviewerIds.includes(context.actorId);
23
+ const readable = owner || manager || reviewer || record.policy.teamVisible;
24
+ if (!readable)
25
+ fail("human_review_not_found", 404);
26
+ await authority.evidence(context, record);
27
+ return { owner, manager, reviewer };
28
+ }
29
+ async function command(context, raw) {
30
+ const input = HumanReviewCommandSchema.parse(raw);
31
+ return repository.transaction(context.scope, async (tx) => {
32
+ await authority.member(context);
33
+ let current = await tx.get(input.id);
34
+ let allowed = { owner: false, manager: false, reviewer: false };
35
+ if (input.action === "create") {
36
+ const spec = { projectId: input.projectId, evidence: input.evidence, form: input.form };
37
+ if (!await authority.owner(context, input.projectId))
38
+ fail("human_assignment_owner_required", 403);
39
+ await authority.evidence(context, spec);
40
+ for (const actorId of new Set([...(input.assigneeId ? [input.assigneeId] : []), ...input.policy.managerIds, ...input.policy.reviewerIds]))
41
+ await authority.assignee(context, actorId, spec);
42
+ if (input.kind === "grade" && !input.evidence.attempts.length)
43
+ fail("human_grade_requires_attempt");
44
+ if (input.kind !== "grade" && !input.evidence.taskIds?.length)
45
+ fail("human_task_assignment_requires_tasks");
46
+ if ((input.form.mode === "pairwise") !== (input.evidence.attempts.length === 2))
47
+ fail("human_form_evidence_mode_mismatch");
48
+ }
49
+ else {
50
+ if (!current)
51
+ fail("human_review_not_found", 404);
52
+ allowed = await access(context, current);
53
+ if (input.action === "bind_author_publication" && !allowed.owner)
54
+ fail("human_task_publication_owner_required", 403);
55
+ if (["reassign", "cancel", "decide"].includes(input.action) && !allowed.manager)
56
+ fail("human_manager_required", 403);
57
+ if (["save_draft", "submit", "reserve_execution", "bind_execution"].includes(input.action) && !allowed.reviewer && !(input.action === "bind_execution" && current.status === "cancelled" && allowed.manager))
58
+ fail("human_assignee_required", 403);
59
+ if (input.action === "claim" && (!current.policy.queueClaim || !current.policy.teamVisible || (current.policy.reviewerIds.length && !current.policy.reviewerIds.includes(context.actorId) && !allowed.owner)))
60
+ fail("human_claim_denied", 403);
61
+ }
62
+ // Authority and evidence are checked before replaying a receipt. Revocation cannot be bypassed by retries.
63
+ const operationKey = contentHash([context.actorId, input.operationId]);
64
+ const requestHash = contentHash(input);
65
+ const receipt = await tx.operation(operationKey);
66
+ if (receipt) {
67
+ if (receipt.requestHash !== requestHash)
68
+ fail("human_operation_conflict", 409);
69
+ const result = await tx.get(receipt.reviewId, receipt.revision);
70
+ if (!result)
71
+ fail("human_operation_result_unavailable", 409);
72
+ return result;
73
+ }
74
+ if ((current?.revision ?? 0) !== input.expectedRevision)
75
+ fail("human_revision_conflict", 409);
76
+ const timestamp = now();
77
+ if (input.action === "create") {
78
+ if (current || input.expectedRevision !== 0)
79
+ fail("human_review_exists", 409);
80
+ current = HumanReviewSchema.parse(sealHumanRecord({ schemaVersion: "openpond.humanReview.v1", id: input.id, revision: 1, scope: context.scope, projectId: input.projectId, createdBy: context.actorId, kind: input.kind, title: input.title, ...(input.dueAt ? { dueAt: input.dueAt } : {}), authorPublication: null, evidence: input.evidence, form: input.form, policy: input.policy, status: input.assigneeId ? "assigned" : "queued", assigneeId: input.assigneeId, generation: 1, drafts: [], submissions: [], decisions: [], audit: [], execution: null, createdAt: timestamp, updatedAt: timestamp }));
81
+ }
82
+ else {
83
+ if (!current)
84
+ fail("human_review_not_found", 404);
85
+ if (closed(current) && input.action !== "bind_author_publication" && !(current.status === "cancelled" && input.action === "bind_execution"))
86
+ fail("human_review_closed", 409);
87
+ const { contentHash: _hash, ...previous } = current;
88
+ let next = { ...previous, revision: current.revision + 1, updatedAt: timestamp };
89
+ if (input.action === "bind_author_publication") {
90
+ if (current.kind !== "author" || current.status !== "accepted" || current.decisions.at(-1)?.taskProposalHash !== input.proposalHash || current.authorPublication)
91
+ fail("human_task_publication_stale", 409);
92
+ if (!authority.publication)
93
+ fail("human_task_publication_owner_unavailable", 422);
94
+ await authority.publication(context, current, input.dataset, input.proposalHash);
95
+ next.authorPublication = { proposalHash: input.proposalHash, dataset: input.dataset, publishedBy: context.actorId, operationId: input.operationId, publishedAt: timestamp };
96
+ }
97
+ else if (input.action === "claim") {
98
+ if (current.assigneeId || current.status !== "queued")
99
+ fail("human_already_claimed", 409);
100
+ await authority.assignee(context, context.actorId, current);
101
+ next = { ...next, assigneeId: context.actorId, status: "assigned", generation: current.generation + 1, audit: [...current.audit, { actorId: context.actorId, action: "claim", priorAssigneeId: current.assigneeId, assigneeId: context.actorId, generation: current.generation + 1, note: "", occurredAt: timestamp }] };
102
+ }
103
+ else if (input.action === "reserve_execution" || input.action === "bind_execution") {
104
+ if (current.kind !== "execute" || input.generation !== current.generation)
105
+ fail("human_execution_assignment_stale", 409);
106
+ if (input.action === "reserve_execution") {
107
+ if (current.execution)
108
+ fail("human_execution_already_reserved", 409);
109
+ next.execution = { executionId: null, operationId: input.runOperationId, boundBy: context.actorId, configurationHash: input.configurationHash, generation: input.generation, result: null };
110
+ next.status = "in_progress";
111
+ }
112
+ else {
113
+ const prior = current.execution;
114
+ if (!prior || prior.executionId || prior.operationId !== input.runOperationId || prior.configurationHash !== input.configurationHash || (prior.boundBy !== context.actorId && !(current.status === "cancelled" && allowed.manager)) || prior.generation !== input.generation)
115
+ fail("human_execution_binding_conflict", 409);
116
+ const binding = { ...prior, executionId: input.executionId };
117
+ if (!authority.execution)
118
+ fail("human_execution_owner_unavailable", 422);
119
+ await authority.execution(context, current, binding);
120
+ next.execution = binding;
121
+ }
122
+ }
123
+ else if (input.action === "reassign") {
124
+ if (current.execution && !current.execution.result)
125
+ fail("human_execution_transfer_requires_completion", 409);
126
+ if (input.assigneeId)
127
+ await authority.assignee(context, input.assigneeId, current);
128
+ next = { ...next, execution: null, assigneeId: input.assigneeId, generation: current.generation + 1, status: input.assigneeId ? "assigned" : "queued", audit: [...current.audit, { actorId: context.actorId, action: "reassign", priorAssigneeId: current.assigneeId, assigneeId: input.assigneeId, generation: current.generation + 1, note: input.note, occurredAt: timestamp }] };
129
+ }
130
+ else if (input.action === "cancel") {
131
+ next.status = "cancelled";
132
+ next.audit = [...current.audit, { actorId: context.actorId, action: "cancel", priorAssigneeId: current.assigneeId, assigneeId: current.assigneeId, generation: current.generation, note: input.note, occurredAt: timestamp }];
133
+ }
134
+ else if (input.action === "save_draft" || input.action === "submit") {
135
+ if (current.kind === "execute" && !current.execution?.executionId)
136
+ fail("human_execution_not_bound", 409);
137
+ if (input.generation !== current.generation)
138
+ fail("human_assignment_generation_stale", 409);
139
+ const latestDecision = current.decisions.at(-1);
140
+ const currentSubmission = current.submissions.filter(s => s.actorId === context.actorId && s.generation === current.generation).at(-1);
141
+ if (currentSubmission && !(latestDecision?.outcome === "request_changes" && latestDecision.submissions.some(s => s.id === currentSubmission.id)))
142
+ fail("human_submission_already_sealed", 409);
143
+ if (input.taskProposal?.tasks.some(task => task.expectedOutput !== undefined) && !allowed.owner)
144
+ fail("human_reference_edit_owner_required", 403);
145
+ if (input.taskProposal || input.action === "submit") {
146
+ try {
147
+ assertHumanTaskProposal(current, input.taskProposal);
148
+ }
149
+ catch (e) {
150
+ fail(e instanceof Error ? e.message : "human_task_proposal_invalid");
151
+ }
152
+ }
153
+ answers(current, input.answers, input.action === "submit");
154
+ if (input.action === "save_draft") {
155
+ next.drafts = [...current.drafts.filter(d => d.actorId !== context.actorId), { actorId: context.actorId, generation: current.generation, ...(input.taskProposal ? { taskProposal: input.taskProposal } : {}), answers: input.answers, note: input.note, updatedAt: timestamp }];
156
+ next.status = current.status === "returned" ? "returned" : "in_progress";
157
+ }
158
+ else {
159
+ if (current.kind === "execute") {
160
+ if (!input.executionResult || !authority.completeExecution)
161
+ fail("human_execution_result_required", 409);
162
+ await authority.completeExecution(context, current, input.executionResult);
163
+ next.execution = { ...current.execution, result: input.executionResult };
164
+ }
165
+ if (input.evidenceHash !== contentHash(current.evidence) || input.formHash !== contentHash(current.form))
166
+ fail("human_review_snapshot_stale", 409);
167
+ const submission = sealHumanRecord({ ...(input.taskProposal ? { taskProposal: input.taskProposal } : {}), id: `submission-${operationKey}`, revision: 1, actorId: context.actorId, generation: current.generation, evidenceHash: input.evidenceHash, formHash: input.formHash, answers: input.answers, note: input.note, submittedAt: timestamp, supersedes: currentSubmission?.id ?? null });
168
+ next.submissions = [...current.submissions, submission];
169
+ next.drafts = current.drafts.filter(d => d.actorId !== context.actorId);
170
+ next.status = current.policy.approval === "none" ? "accepted" : "submitted";
171
+ if (current.policy.approval === "none")
172
+ next.decisions = [...current.decisions, sealHumanRecord({ id: `decision-${operationKey}`, revision: 1, actorId: context.actorId, outcome: "accept", ...(input.taskProposal ? { taskProposalHash: input.taskProposal.contentHash } : {}), submissions: [{ id: submission.id, contentHash: submission.contentHash }], answers: input.answers, note: "", decidedAt: timestamp })];
173
+ }
174
+ }
175
+ else if (input.action === "decide") {
176
+ if (current.policy.approval !== "manager" || current.status !== "submitted")
177
+ fail("human_decision_not_required", 409);
178
+ const selected = input.submissions.map(ref => current.submissions.find(s => s.id === ref.id && s.contentHash === ref.contentHash && s.generation === current.generation && !current.submissions.some(newer => newer.supersedes === s.id)) ?? fail("human_submission_revision_stale", 409));
179
+ if (current.policy.distinctManager && selected.some(s => s.actorId === context.actorId))
180
+ fail("human_distinct_manager_required", 403);
181
+ if (new Set(selected.map(s => s.id)).size !== selected.length)
182
+ fail("human_duplicate_submission");
183
+ if (input.outcome === "accept" && new Set(selected.map(s => s.actorId)).size < current.policy.minimumRaters)
184
+ fail("human_more_raters_required", 409);
185
+ if (input.outcome !== "accept" && !input.note.trim())
186
+ fail("human_decision_reason_required");
187
+ if (current.kind === "author" && input.outcome === "accept" && !selected.some(s => s.taskProposal?.contentHash === input.taskProposalHash))
188
+ fail("human_task_proposal_decision_required", 409);
189
+ if (current.kind !== "author" && input.taskProposalHash)
190
+ fail("human_task_proposal_unexpected");
191
+ answers(current, input.answers, input.outcome === "accept");
192
+ next.decisions = [...current.decisions, sealHumanRecord({ ...(input.taskProposalHash ? { taskProposalHash: input.taskProposalHash } : {}), id: `decision-${operationKey}`, revision: 1, actorId: context.actorId, outcome: input.outcome, submissions: input.submissions, answers: input.answers, note: input.note, decidedAt: timestamp })];
193
+ next.status = input.outcome === "accept" ? "accepted" : input.outcome === "reject" ? "rejected" : "returned";
194
+ }
195
+ current = HumanReviewSchema.parse(sealHumanRecord(next));
196
+ }
197
+ await tx.put(current, input.expectedRevision);
198
+ await tx.saveOperation(operationKey, { requestHash, reviewId: current.id, revision: current.revision });
199
+ return current;
200
+ });
201
+ }
202
+ async function get(context, id, revision) {
203
+ return repository.transaction(context.scope, async (tx) => {
204
+ const current = await tx.get(id);
205
+ if (!current)
206
+ fail("human_review_not_found", 404);
207
+ await access(context, current);
208
+ const result = revision === undefined ? current : await tx.get(id, revision);
209
+ if (!result)
210
+ fail("human_review_not_found", 404);
211
+ await authority.evidence(context, result);
212
+ // Historical snapshots retain prior attribution but cannot reinstate earlier access grants.
213
+ return result;
214
+ });
215
+ }
216
+ async function inbox(context, query) {
217
+ await authority.member(context);
218
+ return repository.transaction(context.scope, async (tx) => {
219
+ let cursor;
220
+ const permitted = [];
221
+ const actionable = new Set();
222
+ let approvalAvailable = false;
223
+ do {
224
+ const page = await tx.list({ projectId: query.projectId, afterId: cursor, limit: 100 });
225
+ for (const record of page.items) {
226
+ let rights;
227
+ try {
228
+ rights = await access(context, record);
229
+ }
230
+ catch (e) {
231
+ if (e instanceof LearningDomainError && [403, 404].includes(e.status))
232
+ continue;
233
+ throw e;
234
+ }
235
+ const active = !closed(record);
236
+ const directlyAssigned = record.assigneeId === context.actorId || record.policy.reviewerIds.includes(context.actorId) || rights.owner && record.assigneeId === null && record.createdBy === context.actorId;
237
+ const myWork = directlyAssigned && rights.reviewer && active && !record.submissions.some(s => s.actorId === context.actorId && s.generation === record.generation && record.status !== "returned");
238
+ const approval = rights.manager && record.policy.approval === "manager" && record.status === "submitted";
239
+ if (rights.manager && record.policy.approval === "manager")
240
+ approvalAvailable = true;
241
+ if (myWork || approval)
242
+ actionable.add(record.id);
243
+ const match = query.view === "mine" ? directlyAssigned : query.view === "approval" ? rights.manager && record.policy.approval === "manager" : record.policy.teamVisible || rights.manager;
244
+ if (match && (query.status === "active" ? active : !active) && (!query.afterId || record.id > query.afterId))
245
+ permitted.push(record);
246
+ }
247
+ cursor = page.nextCursor ?? undefined;
248
+ } while (cursor);
249
+ const items = permitted.slice(0, query.limit);
250
+ return { items, nextCursor: permitted.length > query.limit ? items.at(-1).id : null, actionCount: actionable.size, approvalAvailable };
251
+ });
252
+ }
253
+ async function view(context, record) {
254
+ const current = await get(context, record.id);
255
+ const rights = await access(context, current);
256
+ const privileged = rights.owner || rights.manager;
257
+ const blind = !privileged && current.form.mode === "pairwise" && current.policy.blindPair;
258
+ const independent = !privileged && current.policy.independentRaters;
259
+ return { ...record, evidence: blind ? null : record.evidence, evidenceHash: contentHash(record.evidence), formHash: contentHash(record.form),
260
+ drafts: record.drafts.filter(d => d.actorId === context.actorId).map(d => ({ ...d, taskProposal: rights.owner ? d.taskProposal : redactHumanTaskProposal(d.taskProposal) })),
261
+ submissions: (independent ? record.submissions.filter(s => s.actorId === context.actorId) : record.submissions).map(s => ({ ...s, taskProposal: rights.owner ? s.taskProposal : redactHumanTaskProposal(s.taskProposal) })),
262
+ decisions: independent && !closed(current) ? [] : record.decisions,
263
+ audit: privileged ? record.audit : [], createdBy: blind ? "owner" : record.createdBy,
264
+ permissions: { ...rights, claim: current.status === "queued" && current.policy.queueClaim && current.policy.teamVisible && (!current.policy.reviewerIds.length || current.policy.reviewerIds.includes(context.actorId) || rights.owner) },
265
+ };
266
+ }
267
+ return { command, get, inbox, view };
268
+ }
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { contentHash, ReleaseIdSchema, ReleaseHashSchema } from "@openpond/harness";
3
+ import { assertBoundedTaskJson } from "../task-schema.js";
4
+ export const HumanTaskPatchSchema = z.object({ taskId: ReleaseIdSchema, input: z.record(z.string(), z.unknown()), policyVisibleContext: z.record(z.string(), z.unknown()), expectedOutput: z.record(z.string(), z.unknown()).nullable().optional(), tags: z.array(ReleaseIdSchema).max(100).optional() }).strict();
5
+ export const HumanTaskProposalSchema = z.object({ schemaVersion: z.literal("openpond.humanTaskProposal.v1"), tasks: z.array(HumanTaskPatchSchema).min(1).max(1000), contentHash: ReleaseHashSchema }).strict().superRefine((value, context) => { const { contentHash: hash, ...content } = value; if (hash !== contentHash(content))
6
+ context.addIssue({ code: "custom", message: "Task proposal content hash differs." }); if (new Set(value.tasks.map(t => t.taskId)).size !== value.tasks.length)
7
+ context.addIssue({ code: "custom", message: "Each task may be proposed once." }); try {
8
+ assertBoundedTaskJson(value, 1_048_576);
9
+ }
10
+ catch {
11
+ context.addIssue({ code: "custom", message: "Task proposal exceeds retained data bounds." });
12
+ } });
13
+ export const HumanTaskProposalViewSchema = z.object({ schemaVersion: z.literal("openpond.humanTaskProposal.v1"), tasks: z.array(HumanTaskPatchSchema).min(1).max(1000), contentHash: ReleaseHashSchema, referenceRedacted: z.literal(true).optional() }).strict().superRefine((value, context) => { if (value.referenceRedacted) {
14
+ if (value.tasks.some(t => t.expectedOutput !== undefined))
15
+ context.addIssue({ code: "custom", message: "Redacted proposals cannot include reference outputs." });
16
+ }
17
+ else {
18
+ const parsed = HumanTaskProposalSchema.safeParse(value);
19
+ if (!parsed.success)
20
+ context.addIssue({ code: "custom", message: "Task proposal integrity differs." });
21
+ } });
22
+ export function redactHumanTaskProposal(proposal) { if (!proposal || !proposal.tasks.some(t => t.expectedOutput !== undefined))
23
+ return proposal; return { ...proposal, referenceRedacted: true, tasks: proposal.tasks.map(task => { const { expectedOutput, ...publicTask } = task; void expectedOutput; return publicTask; }) }; }
24
+ export function sealHumanTaskProposal(tasks) { const value = { schemaVersion: "openpond.humanTaskProposal.v1", tasks }; return HumanTaskProposalSchema.parse({ ...value, contentHash: contentHash(value) }); }
25
+ export function assertHumanTaskProposal(record, proposal) { if (record.kind !== "author") {
26
+ if (proposal)
27
+ throw new Error("human_task_proposal_unexpected");
28
+ return;
29
+ } if (!proposal)
30
+ throw new Error("human_task_proposal_required"); const selected = record.evidence.taskIds ?? []; if (proposal.tasks.length !== selected.length || proposal.tasks.some(t => !selected.includes(t.taskId)))
31
+ throw new Error("human_task_proposal_selection_mismatch"); }
32
+ /** Task edits never rewrite the baseline source family, split, private refs,
33
+ * environment or grader contract. The host publishes through its existing owner. */
34
+ export function applyHumanTaskProposal(tasks, proposal) {
35
+ const patches = new Map(proposal.tasks.map(task => [task.taskId, task]));
36
+ if (proposal.tasks.some(patch => !tasks.some(task => task.id === patch.taskId)))
37
+ throw new Error("human_task_proposal_source_unavailable");
38
+ return tasks.map(task => { const patch = patches.get(task.id); return patch ? { ...task, input: patch.input, policyVisibleContext: patch.policyVisibleContext, ...(patch.expectedOutput === undefined ? {} : { expectedOutput: patch.expectedOutput }), ...(patch.tags === undefined ? {} : { tags: patch.tags }) } : task; });
39
+ }
package/dist/index.js CHANGED
@@ -28,3 +28,4 @@ export * from "./tasksets.js";
28
28
  export * from "./task-schema.js";
29
29
  export * from "./rewards.js";
30
30
  export * from "./learning/index.js";
31
+ export * from "./profile-source-candidate.js";
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod";
2
2
  export const JavaScriptVerifierResultSchema = z.object({
3
- score: z.number().finite().min(0).max(1),
3
+ score: z.number().finite().min(0).max(1).nullable(),
4
4
  passed: z.boolean(),
5
5
  feedback: z.string().max(20_000),
6
6
  evidenceRefs: z.array(z.string().max(240)).max(1_000).default([]),
7
- }).strict();
7
+ }).strict().refine(value => value.score !== null || !value.passed, { message: "Unscorable verifiers cannot pass.", path: ["passed"] });
8
8
  /** A process checker must never silently execute under different semantics. */
9
9
  export function assertIsolatedVerifierRuntime(runtime) {
10
10
  if (runtime === "sandbox_process")
@@ -18,8 +18,9 @@ export const RewardAuthoringFieldsSchema = z.object({
18
18
  kind: z.enum(["custom_verifier", "state", "content", "schema", "artifact", "runtime_event", "model_judge", "learned_model", "human"]),
19
19
  fields: z.string(), outputField: z.string(), expectedField: z.string(), expectedValue: z.string(),
20
20
  schema: z.string(), reference: z.string(), events: z.string(), code: z.string(), exportName: z.string(), timeout: z.string(),
21
+ verifierRuntime: z.enum(["isolated_javascript", "sandbox_process"]).optional(),
21
22
  rubric: z.string(), providerId: z.string(), modelId: z.string(), modelRevision: z.string(), temperature: z.string(),
22
- reviewerRole: z.string(), learnedId: z.string(), learnedHash: z.string(), inputContract: z.string(), minimum: z.string(), maximum: z.string(),
23
+ reviewerRole: z.string(), humanForm: z.string().max(131_072).optional(), learnedId: z.string(), learnedHash: z.string(), inputContract: z.string(), minimum: z.string(), maximum: z.string(),
23
24
  fixtures: z.array(RewardFixtureAuthoringFieldsSchema).max(50).optional(),
24
25
  }).strict();
25
26
  export const TaskFormatAuthoringFieldsSchema = z.object({