@openpond/harness 0.2.3 → 0.2.4

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.
@@ -0,0 +1,490 @@
1
+ import { z } from "zod";
2
+ import { contentHash, ImmutableReleaseRefSchema, ReleaseHashSchema, ReleaseIdSchema, ReleaseTimestampSchema, } from "./common.js";
3
+ import { HarnessReviewEvidenceRefSchema, HarnessReviewOwnerScopeSchema, } from "./evaluation-review.js";
4
+ import { HarnessRefinerCapabilitiesSchema, HarnessRefinerEvidenceBasisSchema, } from "./refiner.js";
5
+ import { HarnessImprovementRouteSchema } from "./harness-workspaces.js";
6
+ const ShortTextSchema = z.string().trim().min(1).max(2_000);
7
+ const BoundedTextSchema = z.string().trim().min(1).max(100_000);
8
+ const HarnessRefinerOperationSchema = z.enum(["create", "update", "delete"]);
9
+ const HarnessRefinerInternalRouteSchema = z.enum([
10
+ "memory",
11
+ "prompt",
12
+ "skill",
13
+ "agent",
14
+ ]);
15
+ const HarnessRefinerExternalRouteSchema = z.enum([
16
+ "runtime",
17
+ "product",
18
+ "taskset",
19
+ "training",
20
+ ]);
21
+ export const HarnessRefinerActivityResultSchema = z.enum([
22
+ "no_action",
23
+ "routed",
24
+ "applied",
25
+ "retained",
26
+ "failed",
27
+ ]);
28
+ export const HarnessRefinerCritiqueStatusSchema = z.enum([
29
+ "not_applicable",
30
+ "pending",
31
+ "passed",
32
+ "rejected",
33
+ "failed",
34
+ ]);
35
+ export const HarnessRefinerValidationStatusSchema = z.enum([
36
+ "not_applicable",
37
+ "pending",
38
+ "passed",
39
+ "failed",
40
+ ]);
41
+ export const HarnessRefinerActivityReceiptContentSchema = z
42
+ .object({
43
+ schemaVersion: z.literal("openpond.harnessRefinerActivityReceipt.v1"),
44
+ id: ReleaseIdSchema,
45
+ runRef: ReleaseIdSchema,
46
+ turnId: ReleaseIdSchema,
47
+ result: HarnessRefinerActivityResultSchema,
48
+ decision: z.enum(["no_action", "route", "propose"]).nullable(),
49
+ route: HarnessImprovementRouteSchema.nullable(),
50
+ operation: HarnessRefinerOperationSchema.nullable(),
51
+ target: z.string().trim().min(1).max(2_000).nullable(),
52
+ summary: ShortTextSchema,
53
+ evidenceBasis: HarnessRefinerEvidenceBasisSchema.nullable(),
54
+ critiqueStatus: HarnessRefinerCritiqueStatusSchema,
55
+ validationStatus: HarnessRefinerValidationStatusSchema,
56
+ trigger: ImmutableReleaseRefSchema,
57
+ outcome: ImmutableReleaseRefSchema.nullable(),
58
+ proposal: ImmutableReleaseRefSchema.nullable(),
59
+ applyReceipt: ImmutableReleaseRefSchema.nullable(),
60
+ inputHarness: ImmutableReleaseRefSchema,
61
+ outputHarness: ImmutableReleaseRefSchema.nullable(),
62
+ createdAt: ReleaseTimestampSchema,
63
+ })
64
+ .strict()
65
+ .superRefine((receipt, context) => {
66
+ const proposalResult = receipt.result === "applied" || receipt.result === "retained";
67
+ if (receipt.result === "no_action") {
68
+ requireFields(context, receipt, {
69
+ decision: "no_action",
70
+ route: null,
71
+ operation: null,
72
+ target: null,
73
+ evidenceBasis: null,
74
+ proposal: null,
75
+ applyReceipt: null,
76
+ outputHarness: null,
77
+ critiqueStatus: "not_applicable",
78
+ validationStatus: "not_applicable",
79
+ });
80
+ }
81
+ else if (receipt.result === "routed") {
82
+ if (receipt.decision !== "route" ||
83
+ !HarnessRefinerExternalRouteSchema.safeParse(receipt.route).success ||
84
+ receipt.operation !== null ||
85
+ receipt.target !== null ||
86
+ receipt.evidenceBasis === null ||
87
+ receipt.proposal !== null ||
88
+ receipt.applyReceipt !== null ||
89
+ receipt.outputHarness !== null) {
90
+ context.addIssue({
91
+ code: "custom",
92
+ message: "routed activity requires an external route and evidence basis without proposal state",
93
+ });
94
+ }
95
+ requireFields(context, receipt, {
96
+ critiqueStatus: "not_applicable",
97
+ validationStatus: "not_applicable",
98
+ });
99
+ }
100
+ else if (proposalResult) {
101
+ if (receipt.decision !== "propose" ||
102
+ !HarnessRefinerInternalRouteSchema.safeParse(receipt.route).success ||
103
+ receipt.operation === null ||
104
+ receipt.target === null ||
105
+ receipt.evidenceBasis === null ||
106
+ receipt.proposal === null ||
107
+ receipt.applyReceipt === null) {
108
+ context.addIssue({
109
+ code: "custom",
110
+ message: "applied and retained activity requires complete proposal state",
111
+ });
112
+ }
113
+ if (receipt.result === "applied" && receipt.outputHarness === null) {
114
+ context.addIssue({
115
+ code: "custom",
116
+ message: "applied activity requires the advanced Harness release",
117
+ path: ["outputHarness"],
118
+ });
119
+ }
120
+ if (receipt.result === "applied" &&
121
+ (receipt.critiqueStatus !== "passed" ||
122
+ receipt.validationStatus !== "passed")) {
123
+ context.addIssue({
124
+ code: "custom",
125
+ message: "applied activity requires passed critique and validation",
126
+ });
127
+ }
128
+ if (receipt.result === "retained" && receipt.outputHarness !== null) {
129
+ context.addIssue({
130
+ code: "custom",
131
+ message: "retained activity cannot report a new Harness release",
132
+ path: ["outputHarness"],
133
+ });
134
+ }
135
+ }
136
+ else if (receipt.decision !== null ||
137
+ receipt.route !== null ||
138
+ receipt.operation !== null ||
139
+ receipt.target !== null ||
140
+ receipt.evidenceBasis !== null ||
141
+ receipt.outcome !== null ||
142
+ receipt.proposal !== null ||
143
+ receipt.applyReceipt !== null ||
144
+ receipt.outputHarness !== null) {
145
+ context.addIssue({
146
+ code: "custom",
147
+ message: "failed activity cannot claim a decision, route, proposal, or release transition",
148
+ });
149
+ }
150
+ if (receipt.result !== "failed" && receipt.outcome === null) {
151
+ context.addIssue({
152
+ code: "custom",
153
+ message: "terminal Refiner decisions require an outcome reference",
154
+ path: ["outcome"],
155
+ });
156
+ }
157
+ });
158
+ export const HarnessRefinerActivityReceiptSchema = HarnessRefinerActivityReceiptContentSchema.extend({
159
+ contentHash: ReleaseHashSchema,
160
+ }).strict();
161
+ export const HarnessRefinementCandidateStatusSchema = z.enum([
162
+ "unresolved",
163
+ "confirmed",
164
+ "resolved",
165
+ "rejected",
166
+ "expired",
167
+ ]);
168
+ export const HarnessRefinementCandidateResolutionSchema = z
169
+ .object({
170
+ kind: z.enum([
171
+ "applied_change",
172
+ "later_success",
173
+ "manual_rejection",
174
+ "source_revoked",
175
+ "expired",
176
+ ]),
177
+ reason: BoundedTextSchema,
178
+ evidenceRefs: z.array(ImmutableReleaseRefSchema).max(1_000),
179
+ resolvedAt: ReleaseTimestampSchema,
180
+ })
181
+ .strict();
182
+ export const HarnessRefinementCandidateContentSchema = z
183
+ .object({
184
+ schemaVersion: z.literal("openpond.harnessRefinementCandidate.v1"),
185
+ id: ReleaseIdSchema,
186
+ ownerScope: HarnessReviewOwnerScopeSchema,
187
+ workspaceRef: ReleaseIdSchema,
188
+ fingerprint: ReleaseHashSchema,
189
+ recurrenceFamily: z.string().trim().min(1).max(1_000),
190
+ statement: BoundedTextSchema,
191
+ status: HarnessRefinementCandidateStatusSchema,
192
+ occurrences: z.array(HarnessReviewEvidenceRefSchema).max(1_000),
193
+ counterevidence: z.array(HarnessReviewEvidenceRefSchema).max(1_000),
194
+ sourceReviews: z.array(ImmutableReleaseRefSchema).min(1).max(100),
195
+ relatedHarnessReleases: z.array(ImmutableReleaseRefSchema).max(100),
196
+ firstSeenAt: ReleaseTimestampSchema,
197
+ lastSeenAt: ReleaseTimestampSchema,
198
+ lastReviewedAt: ReleaseTimestampSchema,
199
+ expiresAt: ReleaseTimestampSchema,
200
+ resolution: HarnessRefinementCandidateResolutionSchema.nullable(),
201
+ createdAt: ReleaseTimestampSchema,
202
+ updatedAt: ReleaseTimestampSchema,
203
+ })
204
+ .strict()
205
+ .superRefine((candidate, context) => {
206
+ requireUniqueRefs(context, candidate.occurrences, "occurrences");
207
+ requireUniqueRefs(context, candidate.counterevidence, "counterevidence");
208
+ requireUniqueRefs(context, candidate.sourceReviews, "sourceReviews");
209
+ requireUniqueRefs(context, candidate.relatedHarnessReleases, "relatedHarnessReleases");
210
+ const supportingKeys = new Set(candidate.occurrences.map((item) => item.occurrenceKey));
211
+ if (candidate.counterevidence.some((item) => supportingKeys.has(item.occurrenceKey))) {
212
+ context.addIssue({
213
+ code: "custom",
214
+ message: "supporting occurrences and counterevidence must be disjoint",
215
+ path: ["counterevidence"],
216
+ });
217
+ }
218
+ const actionable = candidate.status === "unresolved" || candidate.status === "confirmed";
219
+ if (actionable && candidate.occurrences.length === 0) {
220
+ context.addIssue({
221
+ code: "custom",
222
+ message: "actionable candidates require at least one supporting occurrence",
223
+ path: ["occurrences"],
224
+ });
225
+ }
226
+ if (actionable &&
227
+ [...candidate.occurrences, ...candidate.counterevidence].some((item) => item.sourcePolicy.state !== "authorized")) {
228
+ context.addIssue({
229
+ code: "custom",
230
+ message: "actionable candidates may contain only currently authorized evidence",
231
+ path: ["occurrences"],
232
+ });
233
+ }
234
+ if (actionable !== (candidate.resolution === null)) {
235
+ context.addIssue({
236
+ code: "custom",
237
+ message: "only resolved, rejected, or expired candidates require a resolution",
238
+ path: ["resolution"],
239
+ });
240
+ }
241
+ if (candidate.status === "expired" &&
242
+ candidate.resolution?.kind !== "expired") {
243
+ context.addIssue({
244
+ code: "custom",
245
+ message: "expired candidates require an expired resolution",
246
+ path: ["resolution", "kind"],
247
+ });
248
+ }
249
+ if (candidate.status === "rejected" &&
250
+ candidate.resolution &&
251
+ !["manual_rejection", "source_revoked"].includes(candidate.resolution.kind)) {
252
+ context.addIssue({
253
+ code: "custom",
254
+ message: "rejected candidates require a rejection or revocation resolution",
255
+ path: ["resolution", "kind"],
256
+ });
257
+ }
258
+ if (candidate.status === "resolved" &&
259
+ candidate.resolution &&
260
+ !["applied_change", "later_success"].includes(candidate.resolution.kind)) {
261
+ context.addIssue({
262
+ code: "custom",
263
+ message: "resolved candidates require applied-change or later-success evidence",
264
+ path: ["resolution", "kind"],
265
+ });
266
+ }
267
+ requireChronology(context, candidate);
268
+ });
269
+ export const HarnessRefinementCandidateSchema = HarnessRefinementCandidateContentSchema.extend({
270
+ contentHash: ReleaseHashSchema,
271
+ }).strict();
272
+ export const HarnessRefinementCandidateLifecycleDecisionSchema = z.enum([
273
+ "created",
274
+ "merged",
275
+ "rejected",
276
+ "expired",
277
+ "reopened",
278
+ "resolved",
279
+ ]);
280
+ export const HarnessRefinementCandidateLifecycleReceiptContentSchema = z
281
+ .object({
282
+ schemaVersion: z.literal("openpond.harnessRefinementCandidateLifecycleReceipt.v1"),
283
+ id: ReleaseIdSchema,
284
+ candidateId: ReleaseIdSchema,
285
+ decision: HarnessRefinementCandidateLifecycleDecisionSchema,
286
+ beforeCandidate: ImmutableReleaseRefSchema.nullable(),
287
+ afterCandidate: ImmutableReleaseRefSchema,
288
+ review: ImmutableReleaseRefSchema,
289
+ addedEvidence: z.array(HarnessReviewEvidenceRefSchema).max(1_000),
290
+ removedEvidence: z.array(ImmutableReleaseRefSchema).max(1_000),
291
+ reason: BoundedTextSchema,
292
+ createdAt: ReleaseTimestampSchema,
293
+ })
294
+ .strict()
295
+ .superRefine((receipt, context) => {
296
+ if ((receipt.decision === "created") !== (receipt.beforeCandidate === null)) {
297
+ context.addIssue({
298
+ code: "custom",
299
+ message: "only candidate creation omits the previous candidate reference",
300
+ path: ["beforeCandidate"],
301
+ });
302
+ }
303
+ if (receipt.beforeCandidate &&
304
+ receipt.beforeCandidate.contentHash === receipt.afterCandidate.contentHash) {
305
+ context.addIssue({
306
+ code: "custom",
307
+ message: "candidate lifecycle transitions must change candidate content",
308
+ path: ["afterCandidate"],
309
+ });
310
+ }
311
+ if (["created", "reopened"].includes(receipt.decision) &&
312
+ receipt.addedEvidence.length === 0) {
313
+ context.addIssue({
314
+ code: "custom",
315
+ message: `${receipt.decision} candidate transitions require added evidence`,
316
+ path: ["addedEvidence"],
317
+ });
318
+ }
319
+ if (receipt.addedEvidence.some((item) => item.sourcePolicy.state !== "authorized")) {
320
+ context.addIssue({
321
+ code: "custom",
322
+ message: "candidate transitions may add only authorized evidence",
323
+ path: ["addedEvidence"],
324
+ });
325
+ }
326
+ requireUniqueRefs(context, receipt.addedEvidence, "addedEvidence");
327
+ requireUniqueRefs(context, receipt.removedEvidence, "removedEvidence");
328
+ const removedKeys = new Set(receipt.removedEvidence.map((item) => refKey(item)));
329
+ if (receipt.addedEvidence.some((item) => removedKeys.has(refKey(item.evidence)))) {
330
+ context.addIssue({
331
+ code: "custom",
332
+ message: "candidate lifecycle evidence cannot be added and removed together",
333
+ path: ["removedEvidence"],
334
+ });
335
+ }
336
+ });
337
+ export const HarnessRefinementCandidateLifecycleReceiptSchema = HarnessRefinementCandidateLifecycleReceiptContentSchema.extend({
338
+ contentHash: ReleaseHashSchema,
339
+ }).strict();
340
+ export const HarnessCrossRunRefinementRequestContentSchema = z
341
+ .object({
342
+ schemaVersion: z.literal("openpond.harnessCrossRunRefinementRequest.v1"),
343
+ id: ReleaseIdSchema,
344
+ ownerScope: HarnessReviewOwnerScopeSchema,
345
+ workspaceRef: ReleaseIdSchema,
346
+ candidate: ImmutableReleaseRefSchema,
347
+ candidateFingerprint: ReleaseHashSchema,
348
+ review: ImmutableReleaseRefSchema,
349
+ admittedHarness: ImmutableReleaseRefSchema,
350
+ evidence: z.array(HarnessReviewEvidenceRefSchema).min(1).max(1_000),
351
+ capabilities: HarnessRefinerCapabilitiesSchema,
352
+ deduplicationKey: ReleaseHashSchema,
353
+ createdAt: ReleaseTimestampSchema,
354
+ })
355
+ .strict()
356
+ .superRefine((request, context) => {
357
+ if (request.evidence.some((item) => item.sourcePolicy.state !== "authorized")) {
358
+ context.addIssue({
359
+ code: "custom",
360
+ message: "cross-run refinement requires currently authorized evidence",
361
+ path: ["evidence"],
362
+ });
363
+ }
364
+ requireUniqueRefs(context, request.evidence, "evidence");
365
+ if (!Object.values(request.capabilities).some(Boolean)) {
366
+ context.addIssue({
367
+ code: "custom",
368
+ message: "cross-run refinement requires at least one available Harness capability",
369
+ path: ["capabilities"],
370
+ });
371
+ }
372
+ const expected = harnessCrossRunRefinementDeduplicationKey(request);
373
+ if (request.deduplicationKey !== expected) {
374
+ context.addIssue({
375
+ code: "custom",
376
+ message: `cross-run refinement deduplicationKey is ${request.deduplicationKey}; expected ${expected}`,
377
+ path: ["deduplicationKey"],
378
+ });
379
+ }
380
+ });
381
+ export const HarnessCrossRunRefinementRequestSchema = HarnessCrossRunRefinementRequestContentSchema.extend({
382
+ contentHash: ReleaseHashSchema,
383
+ }).strict();
384
+ export function createHarnessRefinerActivityReceipt(input) {
385
+ return createHashedContract(input, HarnessRefinerActivityReceiptContentSchema, HarnessRefinerActivityReceiptSchema);
386
+ }
387
+ export function verifyHarnessRefinerActivityReceipt(value) {
388
+ return verifyHashedContract(value, HarnessRefinerActivityReceiptContentSchema, HarnessRefinerActivityReceiptSchema);
389
+ }
390
+ export function createHarnessRefinementCandidate(input) {
391
+ return createHashedContract(input, HarnessRefinementCandidateContentSchema, HarnessRefinementCandidateSchema);
392
+ }
393
+ export function verifyHarnessRefinementCandidate(value) {
394
+ return verifyHashedContract(value, HarnessRefinementCandidateContentSchema, HarnessRefinementCandidateSchema);
395
+ }
396
+ export function createHarnessRefinementCandidateLifecycleReceipt(input) {
397
+ return createHashedContract(input, HarnessRefinementCandidateLifecycleReceiptContentSchema, HarnessRefinementCandidateLifecycleReceiptSchema);
398
+ }
399
+ export function verifyHarnessRefinementCandidateLifecycleReceipt(value) {
400
+ return verifyHashedContract(value, HarnessRefinementCandidateLifecycleReceiptContentSchema, HarnessRefinementCandidateLifecycleReceiptSchema);
401
+ }
402
+ export function harnessCrossRunRefinementDeduplicationKey(input) {
403
+ return contentHash({
404
+ schemaVersion: "openpond.harnessCrossRunRefinementIdentity.v1",
405
+ workspaceRef: input.workspaceRef,
406
+ candidateFingerprint: input.candidateFingerprint,
407
+ admittedHarness: input.admittedHarness,
408
+ });
409
+ }
410
+ export function createHarnessCrossRunRefinementRequest(input) {
411
+ return createHashedContract(input, HarnessCrossRunRefinementRequestContentSchema, HarnessCrossRunRefinementRequestSchema);
412
+ }
413
+ export function verifyHarnessCrossRunRefinementRequest(value) {
414
+ return verifyHashedContract(value, HarnessCrossRunRefinementRequestContentSchema, HarnessCrossRunRefinementRequestSchema);
415
+ }
416
+ function createHashedContract(input, contentSchema, resultSchema) {
417
+ const content = contentSchema.parse(input);
418
+ return resultSchema.parse({
419
+ ...content,
420
+ contentHash: contentHash(content),
421
+ });
422
+ }
423
+ function verifyHashedContract(value, contentSchema, resultSchema) {
424
+ const parsed = resultSchema.safeParse(value);
425
+ if (!parsed.success)
426
+ return false;
427
+ const { contentHash: actual, ...content } = parsed.data;
428
+ return contentHash(contentSchema.parse(content)) === actual;
429
+ }
430
+ function requireFields(context, value, expected) {
431
+ for (const [key, expectedValue] of Object.entries(expected)) {
432
+ if (value[key] === expectedValue)
433
+ continue;
434
+ context.addIssue({
435
+ code: "custom",
436
+ message: `${key} must be ${String(expectedValue)}`,
437
+ path: [key],
438
+ });
439
+ }
440
+ }
441
+ function requireUniqueRefs(context, values, path) {
442
+ const keys = values.map((value) => refKey(value));
443
+ if (new Set(keys).size === keys.length)
444
+ return;
445
+ context.addIssue({
446
+ code: "custom",
447
+ message: `${path} references must be unique`,
448
+ path: [path],
449
+ });
450
+ }
451
+ function refKey(value) {
452
+ if (!value || typeof value !== "object" || Array.isArray(value))
453
+ return String(value);
454
+ const record = value;
455
+ if (typeof record.occurrenceKey === "string")
456
+ return record.occurrenceKey;
457
+ const nested = record.evidence;
458
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
459
+ const evidence = nested;
460
+ return `${String(evidence.id)}:${String(evidence.contentHash)}`;
461
+ }
462
+ return `${String(record.id)}:${String(record.contentHash)}`;
463
+ }
464
+ function requireChronology(context, candidate) {
465
+ const chronology = [
466
+ ["firstSeenAt", candidate.firstSeenAt],
467
+ ["lastSeenAt", candidate.lastSeenAt],
468
+ ["lastReviewedAt", candidate.lastReviewedAt],
469
+ ["updatedAt", candidate.updatedAt],
470
+ ["expiresAt", candidate.expiresAt],
471
+ ];
472
+ for (let index = 1; index < chronology.length; index += 1) {
473
+ const previous = chronology[index - 1];
474
+ const current = chronology[index];
475
+ if (Date.parse(previous[1]) <= Date.parse(current[1]))
476
+ continue;
477
+ context.addIssue({
478
+ code: "custom",
479
+ message: `${current[0]} must not precede ${previous[0]}`,
480
+ path: [current[0]],
481
+ });
482
+ }
483
+ if (Date.parse(candidate.createdAt) > Date.parse(candidate.updatedAt)) {
484
+ context.addIssue({
485
+ code: "custom",
486
+ message: "updatedAt must not precede createdAt",
487
+ path: ["updatedAt"],
488
+ });
489
+ }
490
+ }
@@ -400,9 +400,6 @@ function classifyToolFailure(event) {
400
400
  const result = asRecord(data.result);
401
401
  if (result.timedOut === true)
402
402
  return "timeout";
403
- if (typeof result.exitCode === "number" && result.exitCode !== 0) {
404
- return "command_exit_nonzero";
405
- }
406
403
  const structuredStatus = String(data.status ?? result.status ?? "").toLowerCase();
407
404
  if (["timed_out", "timeout"].includes(structuredStatus))
408
405
  return "timeout";
@@ -413,6 +410,14 @@ function classifyToolFailure(event) {
413
410
  result.stderr,
414
411
  result.stdout,
415
412
  ].filter((value) => typeof value === "string").join("\n").toLowerCase();
413
+ if (text.includes("unicodeencodeerror")
414
+ || text.includes("unicodedecodeerror")
415
+ || text.includes("invalid utf-8")
416
+ || text.includes("invalid utf8")
417
+ || /codec can't (?:en|de)code/.test(text)
418
+ || /can't encode character/.test(text)) {
419
+ return "text_encoding_incompatible";
420
+ }
416
421
  if (text.includes("modulenotfounderror") ||
417
422
  text.includes("module_not_found") ||
418
423
  text.includes("cannot find module") ||
@@ -438,6 +443,9 @@ function classifyToolFailure(event) {
438
443
  if (text.includes("exit code") || text.includes("non-zero") || text.includes("nonzero")) {
439
444
  return "command_exit_nonzero";
440
445
  }
446
+ if (typeof result.exitCode === "number" && result.exitCode !== 0) {
447
+ return "command_exit_nonzero";
448
+ }
441
449
  return "unclassified_tool_failure";
442
450
  }
443
451
  function recoveredClass(deterministicClass) {