@pegma/support-desk-application 0.1.1 → 0.2.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 (86) hide show
  1. package/README.md +237 -7
  2. package/dist/assistant-turn-capacity.d.ts +13 -0
  3. package/dist/assistant-turn-capacity.d.ts.map +1 -0
  4. package/dist/assistant-turn-capacity.js +111 -0
  5. package/dist/assistant-turn-capacity.js.map +1 -0
  6. package/dist/assistant-turn-records.d.ts +68 -0
  7. package/dist/assistant-turn-records.d.ts.map +1 -0
  8. package/dist/assistant-turn-records.js +418 -0
  9. package/dist/assistant-turn-records.js.map +1 -0
  10. package/dist/assistant-turn-types.d.ts +133 -0
  11. package/dist/assistant-turn-types.d.ts.map +1 -0
  12. package/dist/assistant-turn-types.js +2 -0
  13. package/dist/assistant-turn-types.js.map +1 -0
  14. package/dist/assistant-turns.d.ts +42 -0
  15. package/dist/assistant-turns.d.ts.map +1 -0
  16. package/dist/assistant-turns.js +660 -0
  17. package/dist/assistant-turns.js.map +1 -0
  18. package/dist/engineering-evidence.d.ts +20 -0
  19. package/dist/engineering-evidence.d.ts.map +1 -0
  20. package/dist/engineering-evidence.js +899 -0
  21. package/dist/engineering-evidence.js.map +1 -0
  22. package/dist/engineering-followup-internal.d.ts +45 -0
  23. package/dist/engineering-followup-internal.d.ts.map +1 -0
  24. package/dist/engineering-followup-internal.js +2 -0
  25. package/dist/engineering-followup-internal.js.map +1 -0
  26. package/dist/engineering-followup-records.d.ts +209 -0
  27. package/dist/engineering-followup-records.d.ts.map +1 -0
  28. package/dist/engineering-followup-records.js +1434 -0
  29. package/dist/engineering-followup-records.js.map +1 -0
  30. package/dist/engineering-followup-source.d.ts +168 -0
  31. package/dist/engineering-followup-source.d.ts.map +1 -0
  32. package/dist/engineering-followup-source.js +1144 -0
  33. package/dist/engineering-followup-source.js.map +1 -0
  34. package/dist/engineering-followup-types.d.ts +364 -0
  35. package/dist/engineering-followup-types.d.ts.map +1 -0
  36. package/dist/engineering-followup-types.js +2 -0
  37. package/dist/engineering-followup-types.js.map +1 -0
  38. package/dist/engineering-followup-work.d.ts +70 -0
  39. package/dist/engineering-followup-work.d.ts.map +1 -0
  40. package/dist/engineering-followup-work.js +1153 -0
  41. package/dist/engineering-followup-work.js.map +1 -0
  42. package/dist/engineering-internal.d.ts +38 -0
  43. package/dist/engineering-internal.d.ts.map +1 -0
  44. package/dist/engineering-internal.js +11 -0
  45. package/dist/engineering-internal.js.map +1 -0
  46. package/dist/engineering-policy.d.ts +47 -0
  47. package/dist/engineering-policy.d.ts.map +1 -0
  48. package/dist/engineering-policy.js +579 -0
  49. package/dist/engineering-policy.js.map +1 -0
  50. package/dist/engineering-reconciliation.d.ts +6 -0
  51. package/dist/engineering-reconciliation.d.ts.map +1 -0
  52. package/dist/engineering-reconciliation.js +464 -0
  53. package/dist/engineering-reconciliation.js.map +1 -0
  54. package/dist/engineering-records.d.ts +227 -0
  55. package/dist/engineering-records.d.ts.map +1 -0
  56. package/dist/engineering-records.js +1272 -0
  57. package/dist/engineering-records.js.map +1 -0
  58. package/dist/engineering-types.d.ts +505 -0
  59. package/dist/engineering-types.d.ts.map +1 -0
  60. package/dist/engineering-types.js +2 -0
  61. package/dist/engineering-types.js.map +1 -0
  62. package/dist/engineering-work.d.ts +32 -0
  63. package/dist/engineering-work.d.ts.map +1 -0
  64. package/dist/engineering-work.js +1757 -0
  65. package/dist/engineering-work.js.map +1 -0
  66. package/dist/index.d.ts +609 -14
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +3680 -233
  69. package/dist/index.js.map +1 -1
  70. package/dist/resolution-learning-records.d.ts +154 -0
  71. package/dist/resolution-learning-records.d.ts.map +1 -0
  72. package/dist/resolution-learning-records.js +657 -0
  73. package/dist/resolution-learning-records.js.map +1 -0
  74. package/dist/resolution-learning-types.d.ts +356 -0
  75. package/dist/resolution-learning-types.d.ts.map +1 -0
  76. package/dist/resolution-learning-types.js +2 -0
  77. package/dist/resolution-learning-types.js.map +1 -0
  78. package/dist/resolution-learning-worker.d.ts +21 -0
  79. package/dist/resolution-learning-worker.d.ts.map +1 -0
  80. package/dist/resolution-learning-worker.js +1027 -0
  81. package/dist/resolution-learning-worker.js.map +1 -0
  82. package/dist/resolution-learning.d.ts +38 -0
  83. package/dist/resolution-learning.d.ts.map +1 -0
  84. package/dist/resolution-learning.js +860 -0
  85. package/dist/resolution-learning.js.map +1 -0
  86. package/package.json +6 -6
@@ -0,0 +1,657 @@
1
+ import { dataArray, dataObject, encodedBytes, fields, fingerprint, integer, text, timestamp, } from "./assistant-turn-records.js";
2
+ export const resolutionLearningRecordBytes = 60_000;
3
+ export const resolutionLearningCandidateBytes = 16_000;
4
+ export function maximumResolutionLearningSourceBytes(source) {
5
+ const escaped = "\ud800".repeat(200);
6
+ const destinations = source.destinations.map((entry) => ({
7
+ ...entry,
8
+ acceptance: "accepted",
9
+ reconciliation: "confirmed",
10
+ lastReconciliation: {
11
+ fingerprint: "f".repeat(64),
12
+ disposition: "unaffected_publication",
13
+ observedAt: "9999-12-31T23:59:59.999Z",
14
+ proof: { kind: "trusted_cancellation", binding: escaped },
15
+ },
16
+ }));
17
+ return Math.max(encodedBytes(source), encodedBytes({
18
+ ...source,
19
+ destinations,
20
+ sourceGeneration: Number.MAX_SAFE_INTEGER,
21
+ resolutionCycle: Number.MAX_SAFE_INTEGER,
22
+ workGeneration: Number.MAX_SAFE_INTEGER,
23
+ updatedAt: "9999-12-31T23:59:59.999Z",
24
+ state: "withdrawn",
25
+ heldReason: escaped,
26
+ reconciliationPending: false,
27
+ }));
28
+ }
29
+ export function safeLearningSnapshot(value) {
30
+ function inspect(item, depth) {
31
+ if (depth > 20)
32
+ throw new TypeError("Learning data nesting exceeds limit");
33
+ if (item === null || typeof item === "string" || typeof item === "boolean")
34
+ return;
35
+ if (typeof item === "number") {
36
+ if (!Number.isSafeInteger(item))
37
+ throw new TypeError("Learning data numbers must be safe integers");
38
+ return;
39
+ }
40
+ if (typeof item !== "object")
41
+ throw new TypeError("Learning data must be JSON data");
42
+ if (Array.isArray(item)) {
43
+ for (const element of dataArray(item, "learning array", 100))
44
+ inspect(element, depth + 1);
45
+ }
46
+ else
47
+ for (const element of Object.values(dataObject(item, "learning data")))
48
+ if (element !== undefined)
49
+ inspect(element, depth + 1);
50
+ }
51
+ inspect(value, 0);
52
+ return JSON.parse(JSON.stringify(value));
53
+ }
54
+ export const resolutionLearningSourceKey = (ticketId) => ({
55
+ partition: ticketId,
56
+ id: "learning-source",
57
+ });
58
+ export const resolutionLearningWorkKey = (ticketId, generation) => ({
59
+ partition: ticketId,
60
+ id: `resolution-learning:${generation}`,
61
+ });
62
+ export async function resolutionLearningReviewKey(source, target, policyVersion) {
63
+ return {
64
+ partition: source.ticketId,
65
+ id: `learning-review:${await fingerprint({ sourceGeneration: source.sourceGeneration, sourceBindingHash: source.sourceBindingHash, target, policyVersion })}`,
66
+ };
67
+ }
68
+ export function resolveResolutionLearningOptions(input) {
69
+ const value = input === undefined
70
+ ? {}
71
+ : fields(input, "resolutionLearning", [], [
72
+ "enabled",
73
+ "productContext",
74
+ "testReceipts",
75
+ "checks",
76
+ "trustTimeoutMilliseconds",
77
+ "maxGenerations",
78
+ "maxTargets",
79
+ "maxReviewBindings",
80
+ "maxModelAttempts",
81
+ "maxLeaseMilliseconds",
82
+ "maxWorkMilliseconds",
83
+ ]);
84
+ if (value.enabled !== undefined && typeof value.enabled !== "boolean")
85
+ throw new TypeError("resolutionLearning.enabled must be boolean");
86
+ for (const [name, method] of [
87
+ ["productContext", "resolve"],
88
+ ["testReceipts", "verify"],
89
+ ["checks", "verify"],
90
+ ]) {
91
+ if (value[name] !== undefined &&
92
+ (value[name] === null ||
93
+ typeof value[name][method] !== "function"))
94
+ throw new TypeError(`resolutionLearning.${name}.${method} is required`);
95
+ }
96
+ return {
97
+ enabled: value.enabled === true,
98
+ ...(value.productContext === undefined
99
+ ? {}
100
+ : {
101
+ productContext: value.productContext,
102
+ }),
103
+ ...(value.testReceipts === undefined
104
+ ? {}
105
+ : {
106
+ testReceipts: value.testReceipts,
107
+ }),
108
+ ...(value.checks === undefined
109
+ ? {}
110
+ : {
111
+ checks: value.checks,
112
+ }),
113
+ trustTimeoutMilliseconds: integer(value.trustTimeoutMilliseconds ?? 250, "trustTimeoutMilliseconds", 1, 1_000),
114
+ maxGenerations: integer(value.maxGenerations ?? 4, "maxGenerations", 1, 8),
115
+ maxTargets: integer(value.maxTargets ?? 2, "maxTargets", 1, 4),
116
+ maxReviewBindings: integer(value.maxReviewBindings ?? 16, "maxReviewBindings", 1, 64),
117
+ maxModelAttempts: integer(value.maxModelAttempts ?? 2, "maxModelAttempts", 1, 4),
118
+ maxLeaseMilliseconds: integer(value.maxLeaseMilliseconds ?? 60_000, "maxLeaseMilliseconds", 1, 300_000),
119
+ maxWorkMilliseconds: integer(value.maxWorkMilliseconds ?? 300_000, "maxWorkMilliseconds", 1, 3_600_000),
120
+ };
121
+ }
122
+ export function validateResolutionProduct(value) {
123
+ const product = fields(value, "product context", [
124
+ "productId",
125
+ "version",
126
+ "buildId",
127
+ "binding",
128
+ "contextVersion",
129
+ "receiptId",
130
+ "verifiedAt",
131
+ "validUntil",
132
+ ]);
133
+ for (const key of [
134
+ "productId",
135
+ "version",
136
+ "buildId",
137
+ "binding",
138
+ "contextVersion",
139
+ "receiptId",
140
+ ])
141
+ text(product[key], `product.${key}`);
142
+ if (timestamp(product.validUntil, "product.validUntil") <
143
+ timestamp(product.verifiedAt, "product.verifiedAt"))
144
+ throw new TypeError("product context validity is inverted");
145
+ }
146
+ export function validateResolutionEvidence(value) {
147
+ const proof = dataObject(value, "resolution evidence");
148
+ const extra = proof.kind === "customer_confirmation"
149
+ ? ["lastVisibleMessageId"]
150
+ : proof.kind === "staff_verified_fix"
151
+ ? ["solutionMessageId", "verificationReference"]
152
+ : proof.kind === "successful_test"
153
+ ? ["solutionMessageId", "test"]
154
+ : null;
155
+ if (extra === null)
156
+ throw new TypeError("unsupported resolution evidence");
157
+ fields(value, "resolution evidence", ["kind", "actorId", ...extra]);
158
+ text(proof.actorId, "evidence.actorId");
159
+ for (const key of extra.filter((key) => key !== "test"))
160
+ text(proof[key], `evidence.${key}`);
161
+ if (proof.kind === "successful_test") {
162
+ const test = fields(proof.test, "verified test", [
163
+ "executionId",
164
+ "runnerId",
165
+ "testId",
166
+ "productBinding",
167
+ "executedAt",
168
+ "receiptId",
169
+ "verifiedAt",
170
+ "validUntil",
171
+ ]);
172
+ for (const key of [
173
+ "executionId",
174
+ "runnerId",
175
+ "testId",
176
+ "productBinding",
177
+ "receiptId",
178
+ ])
179
+ text(test[key], `test.${key}`);
180
+ timestamp(test.executedAt, "test.executedAt");
181
+ if (timestamp(test.validUntil, "test.validUntil") <
182
+ timestamp(test.verifiedAt, "test.verifiedAt"))
183
+ throw new TypeError("test validity is inverted");
184
+ }
185
+ }
186
+ export function validateResolutionCandidate(value) {
187
+ const candidate = fields(value, "candidate", [
188
+ "problem",
189
+ "applicability",
190
+ "steps",
191
+ "solution",
192
+ "validationRequirements",
193
+ ]);
194
+ const contentText = (entry, name, maximum) => {
195
+ if (typeof entry !== "string" ||
196
+ entry.trim().length === 0 ||
197
+ entry.length > maximum ||
198
+ /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/.test(entry))
199
+ throw new TypeError(`${name} must be bounded nonempty content text`);
200
+ };
201
+ contentText(candidate.problem, "candidate.problem", 4_000);
202
+ contentText(candidate.solution, "candidate.solution", 8_000);
203
+ const applicability = fields(candidate.applicability, "candidate applicability", ["productId", "version", "buildId"]);
204
+ for (const key of ["productId", "version", "buildId"])
205
+ text(applicability[key], `candidate.${key}`);
206
+ for (const key of ["steps", "validationRequirements"])
207
+ for (const item of dataArray(candidate[key], `candidate.${key}`, 16))
208
+ contentText(item, `candidate.${key} item`, 2_000);
209
+ if (encodedBytes(value) > resolutionLearningCandidateBytes)
210
+ throw new TypeError("candidate exceeds 16000 serialized bytes");
211
+ }
212
+ export function validateResolutionReferences(value) {
213
+ const ids = new Set();
214
+ for (const item of dataArray(value, "sourceReferences", 32)) {
215
+ const reference = fields(item, "source reference", [
216
+ "id",
217
+ "system",
218
+ "resourceId",
219
+ "version",
220
+ ]);
221
+ for (const key of ["id", "system", "resourceId", "version"])
222
+ text(reference[key], `sourceReference.${key}`);
223
+ if (ids.has(reference.id))
224
+ throw new TypeError("duplicate source reference ID");
225
+ ids.add(reference.id);
226
+ }
227
+ }
228
+ export function validateResolutionRequest(value) {
229
+ const request = fields(value, "KB request", [
230
+ "operation",
231
+ "articleId",
232
+ "collectionId",
233
+ "draftPayload",
234
+ "sourceReferences",
235
+ "originalExpectedGeneration",
236
+ "commandId",
237
+ "correlationId",
238
+ ]);
239
+ if (request.operation !== "create" && request.operation !== "update")
240
+ throw new TypeError("unsupported KB operation");
241
+ for (const key of ["articleId", "collectionId", "commandId", "correlationId"])
242
+ text(request[key], `request.${key}`);
243
+ text(request.draftPayload, "draftPayload", 24_000);
244
+ let draft;
245
+ try {
246
+ draft = JSON.parse(request.draftPayload);
247
+ }
248
+ catch {
249
+ throw new TypeError("draftPayload must contain valid JSON");
250
+ }
251
+ dataObject(draft, "draft payload");
252
+ integer(request.originalExpectedGeneration, "originalExpectedGeneration", 0);
253
+ if (request.operation === "create" &&
254
+ request.originalExpectedGeneration !== 0)
255
+ throw new TypeError("create requires generation zero");
256
+ validateResolutionReferences(request.sourceReferences);
257
+ }
258
+ export function validateResolutionTarget(value) {
259
+ const target = fields(value, "KB target", [
260
+ "articleId",
261
+ "collectionId",
262
+ "articleRevision",
263
+ "contentHash",
264
+ "sourceSnapshotGeneration",
265
+ "sourceSnapshotHash",
266
+ "orderedReferencesHash",
267
+ ]);
268
+ for (const key of [
269
+ "articleId",
270
+ "collectionId",
271
+ "contentHash",
272
+ "sourceSnapshotHash",
273
+ "orderedReferencesHash",
274
+ ])
275
+ text(target[key], `target.${key}`);
276
+ integer(target.articleRevision, "target.articleRevision", 1);
277
+ integer(target.sourceSnapshotGeneration, "target.sourceSnapshotGeneration", 0);
278
+ }
279
+ /** Decoding checks the physical envelope as strictly as writes. */
280
+ export function assertResolutionLearningRecord(value, key) {
281
+ const row = dataObject(value, "resolution learning record");
282
+ for (const name of ["partition", "ticketId", "id"])
283
+ text(row[name], `record.${name}`);
284
+ if (row.partition !== key.partition ||
285
+ row.id !== key.id ||
286
+ row.ticketId !== key.partition)
287
+ throw new TypeError("learning record envelope mismatch");
288
+ if (row.kind === "resolution_learning_review") {
289
+ fields(row, "review record", [
290
+ "kind",
291
+ "partition",
292
+ "id",
293
+ "ticketId",
294
+ "sourceGeneration",
295
+ "sourceBindingHash",
296
+ "target",
297
+ "decision",
298
+ "policyVersion",
299
+ "reviewerId",
300
+ "reviewedAt",
301
+ ], ["reason"]);
302
+ if (!/^learning-review:[0-9a-f]{64}$/.test(row.id))
303
+ throw new TypeError("Invalid learning review key");
304
+ integer(row.sourceGeneration, "sourceGeneration", 1);
305
+ text(row.sourceBindingHash, "sourceBindingHash");
306
+ validateResolutionTarget(row.target);
307
+ text(row.policyVersion, "policyVersion");
308
+ text(row.reviewerId, "reviewerId");
309
+ timestamp(row.reviewedAt, "reviewedAt");
310
+ if (row.decision !== "approve" && row.decision !== "reject")
311
+ throw new TypeError("Invalid review decision");
312
+ if (row.reason !== undefined)
313
+ text(row.reason, "reason");
314
+ if (encodedBytes(row) > 60_000)
315
+ throw new TypeError("Review record exceeds limit");
316
+ return;
317
+ }
318
+ integer(row.sourceGeneration, "sourceGeneration", 0);
319
+ integer(row.resolutionCycle, "resolutionCycle", 1);
320
+ integer(row.workGeneration, "workGeneration", 1);
321
+ timestamp(row.createdAt, "createdAt");
322
+ if (row.kind === "resolution_learning_source") {
323
+ fields(row, "learning source", [
324
+ "kind",
325
+ "partition",
326
+ "id",
327
+ "ticketId",
328
+ "sourceId",
329
+ "resolutionCycle",
330
+ "sourceGeneration",
331
+ "workGeneration",
332
+ "generationCount",
333
+ "reviewBindingCount",
334
+ "state",
335
+ "createdAt",
336
+ "updatedAt",
337
+ "destinations",
338
+ "reconciliationPending",
339
+ ], [
340
+ "heldReason",
341
+ "resolvedAt",
342
+ "acceptedTicketRevision",
343
+ "proofIdentity",
344
+ "sourceBindingHash",
345
+ "evidenceFingerprint",
346
+ "productContextFingerprint",
347
+ "evidence",
348
+ "product",
349
+ "pendingProofReference",
350
+ ]);
351
+ if (row.id !== "learning-source" ||
352
+ !["unverified", "eligible", "held", "withdrawn"].includes(row.state))
353
+ throw new TypeError("invalid learning source");
354
+ text(row.sourceId, "sourceId");
355
+ for (const name of [
356
+ "heldReason",
357
+ "proofIdentity",
358
+ "sourceBindingHash",
359
+ "evidenceFingerprint",
360
+ "productContextFingerprint",
361
+ ])
362
+ if (row[name] !== undefined)
363
+ text(row[name], name);
364
+ if (row.pendingProofReference !== undefined)
365
+ text(row.pendingProofReference, "pendingProofReference", 208);
366
+ if (row.acceptedTicketRevision !== undefined)
367
+ integer(row.acceptedTicketRevision, "acceptedTicketRevision", 1);
368
+ if (row.resolvedAt !== undefined)
369
+ timestamp(row.resolvedAt, "resolvedAt");
370
+ integer(row.generationCount, "generationCount", 0, 8);
371
+ timestamp(row.updatedAt, "updatedAt");
372
+ integer(row.reviewBindingCount, "reviewBindingCount", 0, 64);
373
+ if (typeof row.reconciliationPending !== "boolean")
374
+ throw new TypeError("invalid reconciliation marker");
375
+ if (row.reconciliationPending !==
376
+ row.destinations.some((entry) => entry.reconciliation !== "confirmed"))
377
+ throw new TypeError("reconciliation marker disagrees with destinations");
378
+ for (const target of dataArray(row.destinations, "destinations", 4)) {
379
+ const destination = fields(target, "destination", [
380
+ "articleId",
381
+ "sourceGeneration",
382
+ "stepId",
383
+ "requestHash",
384
+ "acceptance",
385
+ "reconciliation",
386
+ ], ["lastReconciliation"]);
387
+ for (const name of ["articleId", "stepId", "requestHash"])
388
+ text(destination[name], `destination.${name}`);
389
+ integer(destination.sourceGeneration, "destination.sourceGeneration", 1);
390
+ if (!["unknown", "accepted"].includes(destination.acceptance) ||
391
+ !["pending", "blocked", "confirmed"].includes(destination.reconciliation))
392
+ throw new TypeError("invalid destination state");
393
+ if (destination.lastReconciliation !== undefined) {
394
+ const last = fields(destination.lastReconciliation, "reconciliation", ["fingerprint", "disposition", "observedAt"], ["proof"]);
395
+ text(last.fingerprint, "reconciliation.fingerprint");
396
+ timestamp(last.observedAt, "reconciliation.observedAt");
397
+ if (![
398
+ "blocked",
399
+ "retired",
400
+ "unaffected_publication",
401
+ "write_rejected",
402
+ ].includes(last.disposition))
403
+ throw new TypeError("Invalid reconciliation disposition");
404
+ if (last.proof !== undefined) {
405
+ const proof = fields(last.proof, "reconciliation proof", [
406
+ "kind",
407
+ "binding",
408
+ ]);
409
+ text(proof.binding, "proof.binding");
410
+ if (![
411
+ "terminal_receipt",
412
+ "revision_fence",
413
+ "trusted_cancellation",
414
+ ].includes(proof.kind))
415
+ throw new TypeError("Invalid reconciliation proof");
416
+ }
417
+ }
418
+ }
419
+ }
420
+ else if (row.kind === "resolution_learning_work") {
421
+ fields(row, "learning work", [
422
+ "kind",
423
+ "partition",
424
+ "id",
425
+ "ticketId",
426
+ "sourceGeneration",
427
+ "resolutionCycle",
428
+ "sourceBindingHash",
429
+ "workGeneration",
430
+ "acceptedTicketRevision",
431
+ "workingSolutionMessageIds",
432
+ "createdAt",
433
+ "deadline",
434
+ "status",
435
+ "stage",
436
+ "modelAttempts",
437
+ "claimGeneration",
438
+ ], [
439
+ "evidence",
440
+ "product",
441
+ "claim",
442
+ "leaseUntil",
443
+ "retryAt",
444
+ "stepId",
445
+ "requestHash",
446
+ "candidate",
447
+ "candidateHash",
448
+ "exactRequest",
449
+ "withdrawnRequest",
450
+ "usageTombstones",
451
+ "verifiedCheckReceipt",
452
+ "checkPolicyVersion",
453
+ "targetDecisionHash",
454
+ "acknowledgement",
455
+ "target",
456
+ "heldReason",
457
+ "lastCompletion",
458
+ "lastFinish",
459
+ ]);
460
+ if (row.id !== `resolution-learning:${row.sourceGeneration}` ||
461
+ ![
462
+ "pending",
463
+ "claimed",
464
+ "retry",
465
+ "candidate",
466
+ "prepared",
467
+ "completed",
468
+ "held",
469
+ "unknown",
470
+ "superseded",
471
+ ].includes(row.status) ||
472
+ (row.stage !== "model" && row.stage !== "kb_delivery"))
473
+ throw new TypeError("invalid learning work");
474
+ text(row.sourceBindingHash, "sourceBindingHash");
475
+ for (const name of [
476
+ "heldReason",
477
+ "stepId",
478
+ "requestHash",
479
+ "candidateHash",
480
+ "verifiedCheckReceipt",
481
+ "checkPolicyVersion",
482
+ "targetDecisionHash",
483
+ ])
484
+ if (row[name] !== undefined)
485
+ text(row[name], name);
486
+ for (const name of ["lastCompletion", "lastFinish"])
487
+ if (row[name] !== undefined) {
488
+ const receipt = fields(row[name], name, ["fingerprint"]);
489
+ text(receipt.fingerprint, `${name}.fingerprint`);
490
+ }
491
+ if (row.acknowledgement !== undefined) {
492
+ const acknowledgement = fields(row.acknowledgement, "acknowledgement", [
493
+ "id",
494
+ "generation",
495
+ "articleRevision",
496
+ "contentHash",
497
+ "state",
498
+ ]);
499
+ for (const name of ["id", "contentHash", "state"])
500
+ text(acknowledgement[name], `acknowledgement.${name}`);
501
+ integer(acknowledgement.generation, "acknowledgement.generation", 1);
502
+ integer(acknowledgement.articleRevision, "acknowledgement.articleRevision", 1);
503
+ }
504
+ integer(row.acceptedTicketRevision, "acceptedTicketRevision", 1);
505
+ integer(row.claimGeneration, "claimGeneration", 0);
506
+ timestamp(row.deadline, "deadline");
507
+ for (const messageId of dataArray(row.workingSolutionMessageIds, "workingSolutionMessageIds", 16))
508
+ text(messageId, "solution message ID");
509
+ for (const item of dataArray(row.modelAttempts, "modelAttempts", 4)) {
510
+ const attempt = fields(item, "attempt", [
511
+ "preparedClaimGeneration",
512
+ "stepId",
513
+ "requestHash",
514
+ "binding",
515
+ "accounting",
516
+ ], ["processing"]);
517
+ integer(attempt.preparedClaimGeneration, "preparedClaimGeneration", 1);
518
+ text(attempt.stepId, "attempt.stepId");
519
+ text(attempt.requestHash, "attempt.requestHash");
520
+ const binding = fields(attempt.binding, "attempt.binding", [
521
+ "inputManifest",
522
+ "inputFingerprint",
523
+ "inputManifestHash",
524
+ "configVersion",
525
+ "promptVersion",
526
+ "modelVersion",
527
+ "checkPolicyVersion",
528
+ "budgetsHash",
529
+ "usageReservationId",
530
+ "deadline",
531
+ ]);
532
+ timestamp(binding.deadline, "binding.deadline");
533
+ for (const name of Object.keys(binding).filter((name) => name !== "inputManifest" && name !== "deadline"))
534
+ text(binding[name], `binding.${name}`);
535
+ const manifest = fields(binding.inputManifest, "inputManifest", [
536
+ "messages",
537
+ "workingSolutionMessageIds",
538
+ "product",
539
+ ]);
540
+ for (const message of dataArray(manifest.messages, "manifest.messages", 100)) {
541
+ const reference = fields(message, "manifest message", [
542
+ "messageId",
543
+ "ordinal",
544
+ ]);
545
+ text(reference.messageId, "messageId");
546
+ integer(reference.ordinal, "ordinal", 1);
547
+ }
548
+ for (const id of dataArray(manifest.workingSolutionMessageIds, "manifest solutions", 16))
549
+ text(id, "manifest solution");
550
+ const product = fields(manifest.product, "manifest product", [
551
+ "productId",
552
+ "version",
553
+ "buildId",
554
+ "binding",
555
+ "contextVersion",
556
+ ]);
557
+ for (const name of Object.keys(product))
558
+ text(product[name], `manifest.product.${name}`);
559
+ if (!["not_called", "accounted", "unknown"].includes(attempt.accounting))
560
+ throw new TypeError("invalid attempt accounting");
561
+ if (attempt.processing !== undefined) {
562
+ const processing = fields(attempt.processing, "processing", [], [
563
+ "provider",
564
+ "model",
565
+ "requestId",
566
+ "failureCategory",
567
+ "currency",
568
+ "inputTokens",
569
+ "outputTokens",
570
+ "costMicros",
571
+ ]);
572
+ for (const [name, entry] of Object.entries(processing))
573
+ if (["inputTokens", "outputTokens", "costMicros"].includes(name))
574
+ integer(entry, `processing.${name}`, 0);
575
+ else
576
+ text(entry, `processing.${name}`);
577
+ }
578
+ }
579
+ if (row.claim !== undefined) {
580
+ const claim = fields(row.claim, "claim", [
581
+ "ticketId",
582
+ "sourceGeneration",
583
+ "workerId",
584
+ "claimId",
585
+ "claimGeneration",
586
+ ]);
587
+ for (const name of ["ticketId", "workerId", "claimId"])
588
+ text(claim[name], `claim.${name}`);
589
+ integer(claim.sourceGeneration, "claim.sourceGeneration", 1);
590
+ integer(claim.claimGeneration, "claim.claimGeneration", 1);
591
+ if (claim.ticketId !== row.ticketId ||
592
+ claim.sourceGeneration !== row.sourceGeneration ||
593
+ claim.claimGeneration !== row.claimGeneration ||
594
+ row.leaseUntil === undefined)
595
+ throw new TypeError("claim envelope mismatch");
596
+ }
597
+ for (const name of ["leaseUntil", "retryAt"])
598
+ if (row[name] !== undefined)
599
+ timestamp(row[name], name);
600
+ if (row.withdrawnRequest !== undefined) {
601
+ const request = fields(row.withdrawnRequest, "withdrawn request", [
602
+ "operation",
603
+ "articleId",
604
+ "collectionId",
605
+ "sourceReferences",
606
+ "originalExpectedGeneration",
607
+ "commandId",
608
+ "correlationId",
609
+ ]);
610
+ for (const name of [
611
+ "articleId",
612
+ "collectionId",
613
+ "commandId",
614
+ "correlationId",
615
+ ])
616
+ text(request[name], `withdrawn.${name}`);
617
+ if (request.operation !== "create" && request.operation !== "update")
618
+ throw new TypeError("invalid withdrawn operation");
619
+ integer(request.originalExpectedGeneration, "originalExpectedGeneration", 0);
620
+ validateResolutionReferences(request.sourceReferences);
621
+ }
622
+ if (row.usageTombstones !== undefined)
623
+ for (const item of dataArray(row.usageTombstones, "usageTombstones", 4)) {
624
+ const usage = fields(item, "usage tombstone", [
625
+ "stepId",
626
+ "requestHash",
627
+ "usageReservationId",
628
+ "accounting",
629
+ ]);
630
+ for (const name of ["stepId", "requestHash", "usageReservationId"])
631
+ text(usage[name], `usage.${name}`);
632
+ if (!["not_called", "accounted", "unknown"].includes(usage.accounting))
633
+ throw new TypeError("invalid usage accounting");
634
+ }
635
+ if (row.candidate !== undefined)
636
+ validateResolutionCandidate(row.candidate);
637
+ if (row.exactRequest !== undefined) {
638
+ validateResolutionRequest(row.exactRequest);
639
+ if (row.candidate !== undefined)
640
+ throw new TypeError("candidate body duplicated in delivery state");
641
+ }
642
+ if (row.target !== undefined)
643
+ validateResolutionTarget(row.target);
644
+ }
645
+ else
646
+ throw new TypeError("invalid learning record kind");
647
+ if (row.evidence !== undefined)
648
+ validateResolutionEvidence(row.evidence);
649
+ if (row.product !== undefined)
650
+ validateResolutionProduct(row.product);
651
+ if (encodedBytes(value) > resolutionLearningRecordBytes)
652
+ throw new TypeError("learning record exceeds serialized limit");
653
+ if (row.kind === "resolution_learning_source" &&
654
+ maximumResolutionLearningSourceBytes(value) > resolutionLearningRecordBytes)
655
+ throw new TypeError("source lacks in-place invalidation capacity");
656
+ }
657
+ //# sourceMappingURL=resolution-learning-records.js.map